StringBuffer 클래스와 StringBulider 클래스
String클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 변경이 가능하다. 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer인스턴스를 생성할 때 그 크기를 지정할 수 있다.
이 떄 , 편집할 문자열의 길이를 고려하여 버퍼의 길이를 충분히 잡아주는 것이 좋다. 편집 중인 문자열이 버퍼의 길이를 넘어서게 되면 버퍼의 길이를 늘려주는 작업이 추가로 수행되어야하기 때문에 작업효율이 떨어진다.
StringBuffer클래스는 String클래스와 유사한 점이 많다. 아래의 코드에서 알 수 있듯이, StringBuffer클래스는 String클래스와 같이 문자열을 저장하기 위한 char형 배열의 참조변수를 인스턴스변수로 선언해놓고 있다. StringBuffer 인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스변수 value가 참조하게 된다.
public final class StringBuffer implements java.io.Serializable{
private char[] value;
..
}
StringBuffer의 생성자
StringBuffer클래스의 인스턴스를 생성할 때, 적당한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer)로 사용된다.
StringBuffer인스턴스를 생성할 때는 생성자 StringBuffer(int length)를 사용해서 StringBuffer인스턴스에 저장될 문자열의 길이를 고려하여 충분히 여유있는 크기로 지정하는 것이 좋다. StringBuffer인스턴스를 생성할 때, 버퍼의 크기를 지정해주지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.
public StringBuffer(int length){
value = new char[length];
shared =false;
}
public StringBuffer(){
this(16); //버퍼의 크기를 지정하지 않으면 버퍼의 크기는 16이된다.
}
public StringBuffer(String str){
this(str.length() + 16); //지정된 문자열의 길이보다 16더 크게 버퍼를 생성한다
}
아래의 코드는 StringBuffer클래스의 일부인데, 버퍼의 크기를 변경하는 내용의 코드이다.
StringBuffer인스턴스로 문자열을 다루는 작업을 할 때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다.
배열의 길이는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후 에 이전 배열의 값을 복사해야 한다.
..
// 새로운 길이(newCapacity)의 배열을 생성한다. newCapacity는 정수값이다.
char newValue[] = new Char[newCapacity];
//배열 value의 내용을 newValue로 복사한다.
System.arraycopy(value,0,newValue,0,count); //count는 문자열의 길이
value = newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장
이렇게 함으로써 StringBuffer클래스의 인스턴스변수 value는 길이가 증가된 새로운 배열을 참조하게 된다.
StringBuffer의 변경
String과 달리 StringBuffer는 내용을 변경할 수 있다. 예를 들어 아래와 같이 StringBuffer를 생성하였다고 가정하자.
StringBuffer sb =new StringBuffer("abc");
그리고 sb에 문자열 "123"을 추가하면
sb.append("123"); //sb의 내용 뒤에 "123"을 추가한다
append()는 반환타입이 StringBuffer인데 자신의 주소를 반환한다. 그래서 아래와 같은 문장이 수행되면, sb에 새로운 문자열이 추가되고 sb자신의 주소를 반환하여 sb2에는 sb의 주소인 0x100이 저장된다.
StringBuffer sb2 = sb.append("ZZ"): //sb의 내용뒤에 "zz"를 추가된다.
System.out.println(sb); //abc123zz
System.out.println(sb2); //abc123zz
내부적으로 sb, sb2는 모두 0x100주소를 가리키고 있다.
sb와 sb2가 모두 같은 StringBuffer인스턴스를 가리키고 있으므로 같은 내용이 출력된다. 그래서 하나의 StringBuffer인스턴스에 대해 아래와 같이 연속적으로 append()를 호출하는 것이 가능하다.
StringBuffer sb =new StringBuffer("abc");
sb.append("123");
sb.append("ZZ");
----------------------------------
StringBuffer sb =new StringBuffer("abc");
sb.append("123").append("ZZ");
---------------
sb
아래의 코드 점선으로 친 부분이 sb이므로 여기에 다시 append()를 호출할 수 있는 것이다.
만일 append()의 반환타입이 void라면 이렇게 할 수 없었을 것이다.
- StringBuffer클래스에는 append()처럼 객체 자신을 반환하는 메서드들이 많다.
StringBuffer의 비교
String인스턴스 간의 비교에 대해서 학습하면서 등가비교 연산자 ==에 의핸 비교와 equals메서드에 의한 비교의 차이점을 자세히 알아봤다.
String클래스에서는 equals메서드를 오버라이딩해서 문자열의 내용을 비교하도록 구현되어 있지만, StringBuffer클래스는 equals메서드를 오버라이딩 하지 않아서 StringBuffer클래스의 equals메서드를 사용해도 등가비교연산자(==)로 비교한 것과 같은 결과를 얻는다.
StringBuffer sb =new StringBuffer("abc");
StringBuffer sb2 =new StringBuffer("abc");
System.out.println(sb == sb2); //false
System.out.println(sb.equals(sb2)); //false
반면에 toString()은 오버라이딩되어 있어서 StringBuffer인스턴스에 toString()을 호출하면 , 담고 있는 문자열을 String으로 반환한다.
그래서 StringBuffer인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer인스턴스에 toString()을 호출해서 String인스턴스를 얻은다음, 여기에 equals메서드를 사용해서 비교해야 한다.
String s= sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2)); //true
import javax.net.ssl.SSLContext;
public class StringBufferEx1 {
public static void main(String[] args) {
StringBuffer sb =new StringBuffer("abc");
StringBuffer sb2 =new StringBuffer("abc");
System.out.println("sb == sb2 ? "+(sb==sb2));
System.out.println("sb.equals(sb2) ? "+sb.equals(sb2));
//StringBuffer의 내용을 String으로 변환한다.
String s = sb.toString(); //String s = new String(sb)와 같다.
String s2 = sb2.toString();
System.out.println("s.equals(s2) ? "+s.equals(s2));
}
}
============================
sb == sb2 ? false
sb.equals(sb2) ? false
s.equals(s2) ? true
StringBuffer클래스의 생성자와 메서드
StringBuffer클래스 역시 문자열을 다루기 위한 것이기 때문에 String클래스와 유사한 메서드를 많이 가지고 있다. 그리고 StringBuffer는 추가, 변경, 삭제와 같이 저장된 내용을 변경할 수 있는 메서드들이 추가로 제공된다.
메서드 | 설명 | 예제/결과 |
StringBuffer() | 16문자를 담을 수 있는 버퍼를 가진 StringBuffer인스턴스를 생성한다. | StringBuffer sb = new StringBuffer(); ———————————————————— sb= "" |
StringBuffer(int length) | 지정된 개수의 문자를 담을 수 있는 버퍼를 가진 StringBuffer인스턴스를 생성한다. | StringBuffer sb =new StringBuffer(10); ————————————————————— sb= "" |
StringBuffer(String str) | 지정된 문자열 값(str)을 갖는 StringBuffer인스턴스를 생성한다. | StringBuffer sb =new StringBuffer("Hi"); ————————————————————— sb = "Hi" |
StringBuffer append(boolean b) StringBuffer append(char c) StringBuffer append(char[] str) StringBuffer append(double d) StringBuffer append(float f) StringBuffer append(int i) StringBuffer append(long l) StringBuffer append(Object obj) StringBuffer append(String str) |
매개변수로 입력된 값을 문자열로 변환하여 StringBuffer인스턴스가 저장하고 있는 문자열의 뒤에 덧붙인다. | StringBuffer sb= new StringBuffer("abc"); StringBuffer sb2= sb.append(true); sb.append('d').append(10.0f); StringBuffer sb3 = sb.append("ABC").append(123) —————————————- sb = "abctrue10.0ABC123" sb2 = "abctrue10.0ABC123" sb3 = "abctrue10.0ABC123" |
int capacity() | StringBuffer인스턴스 버퍼크기를 알려준다. length()는 버퍼에 담긴 문자열의 길이를 알려준다. | StringBuffer sb= new StringBuffer(100); sb.append("abcd"); int buffersize = sb.capacity(); int stringSize = sb.length(); —————————— bufferSize = 100 stringSize = 4 (sb에 담긴 문자열이 "abcd"이므로) |
char charAt(int index) | 지정된 위치(index)에 있는 문자를 반환한다. | StringBuffer sb = new StringBuffer("abc"); char c = sb.charAt(2); ———————————————- c = 'c' |
StringBuffer delete(int start, int end) | 시작위치(start)부터 끝 위치(end)사이에 있는 문자를 제거한다. 단 끝위치의 문자는 제외 | String sb =new String("123456"); StringBuffer sb2 = sb.delete(3,6); ————————————————————— sb = "0126" sb2= "0126" |
StringBuffer deleteCharAt(int index) | 지정된 위치(index)의 문자를 제거한다. | StringBuffer sb= new StringBuffer("0123456"); sb.deleteCharAt(3); ———————————————————— sb = "012456" |
StringBuffer insert(int pos, boolean b) StringBuffer insert(int pos, char c) StringBuffer insert(int pos, char[] str) StringBuffer insert(int pos, double d) StringBuffer insert(int pos, float f) StringBuffer insert(int pos, int i) StringBuffer insert(int pos, long l) StringBuffer insert(int pos, Object obj) StringBuffer insert(int pos, String str) | 두번째 매개변수로 받은 값을 문자열로 변환하여 지정된 위치(pos)에 추가한다. pos는 0부터 시작 | StringBuffer sb= new StringBuffer("0123456"); sb.insert(4, '.'); ————————————————————— sb="0123.456" |
int length() | StringBuffer인스턴스에 저장되어 있는 문자열의 길이를 반환한다 | StringBuffer sb= new StringBuffer("0123456"); int length = sb.length(); —————————————————————length = 7 |
StringBuffer replace(int start, int end, String str) | 지정된 범위(start~end)의 문자들을 주어진 문자열로 바꾼다. end위치의 문자는 범위에 포함되지 않는다.(start ≤ x <end) | StringBuffer sb =new StringBuffer("0123456"); sb.replace(3,6,"AB"); —————————————————————sb = "012AB6" "345"를 "AB"로 바꾼다 |
StringBuffer reverse() | StringBuffer 인스턴스에 저장되어 있는 문자열의 순서를 거꾸로 나열한다 | StringBuffer sb = new StringBuffer("0123456"); sb.reverse(); ————————————————————— sb = "6543210" |
void setCharAt(int index, char ch) | 지정된 위치의 문자를 주어진 문자(ch)로 바꾼다. | StringBuffer sb =new StringBuffer("0123456"); sb.setCharAt(5, 'o'); —————————————————————sb = "01234o6" |
void setLength(int newLength) | 지정된 길이로 문자열의 길이를 변경한다. 길이를 늘리는 경우에 나머지 빈 공간을 널문자 '\u0000'로 채운다 | StringBuffer sb = new StringBuffer("0123456"); sb.setLength(5); StringBuffer sb2= new StringBuffer("0123456"); sb2.setLenth(10); String str = sb2.toString().trim(); ————————————————————— sb= "01234" sb2="0123456 " str = "0123456" |
String toString() | StringBuffer인스턴스의 문자열을 String으로 반환 | StringBuffer sb = new StringBuffer("0123456"); String str = sb.toString(); ————————————————————— str = "0123456" |
String substring(int start) String substring(int start, int end) |
지정된 범위 내의 문자열을 String으로 뽑아서 반환한다. 시작위치(start)만 지정하면 시작위치부터 문자열 끝까지 뽑아서 반환한다. | StringBuffer sb =new StringBuffer("0123456"); String str = sb.substring(3); String str2 = sb.substring(3,5); ————————————————————— str = "3456" str2 = "34" |
public class StringBufferEx2 {
public static void main(String[] args) {
StringBuffer sb =new StringBuffer("01");
StringBuffer sb2 =new StringBuffer("23");
sb.append('4').append(56);
StringBuffer sb3 = sb.append(78);
sb3.append(9.0);
System.out.println("sb ="+sb);
System.out.println("sb2="+sb2);
System.out.println("sb3="+sb3);
System.out.println("sb="+sb.deleteCharAt(8));
System.out.println("sb="+sb.delete(3, 6));
System.out.println("sb="+sb.insert(3, "abc"));
System.out.println("sb="+sb.replace(6,sb.length(), "END"));
System.out.println("capacity="+sb.capacity());
System.out.println("length="+sb.length());
}
}
=====================
sb =01456789.0
sb2=23
sb3=01456789.0
sb=014567890
sb=014890
sb=014abc890
sb=014abcEND
capacity=18
length=9
StringBuilder란?
StingBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화 되어 있다. 동기화가 StringBuffer의 성능을 떨어트린다. 멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 불필요하게 성능만 떨어트리게 된다.
그래서 StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.
StringBuilder는 StringBuffer와 완전히 똑같은 기능으로 작성되어 있어서, 소스코드에서 STringBuffer대신 StringBuilder를 사용하도록 바꾸기만 하면된다. 즉 ,StringBuffer 타입의 참조변수를 선언한 부분과 StringBuffer의 생성자만 바꾸면 된다는 말이다.
StringBuffer sb;
sb = new StringBuffer();
sb.append("abc");
----------------------------
StringBulider sb;
sb = new StringBuilder();
sb.append("abc");
StringBuffer도 충분히 성능이 좋기 때문에 성능향상이 반드시 필요한 경우를 제외하고는 기존에 작성한 코드에서 StringBuffer를 StringBuilder로 굳이 바꿀필요는 없다.
Math클래스
Math클래스는 기본적인 수학계산에 유용한 메서드로 구성되어 있다. 임의의 수를 얻을수 있는 random()과 반올림에 사용되는 round() 등은 이미 학습하였다.
Math클래스의 생성자는 접근 제어자가 private이기 때문에 다른 클래스에서 Math인스턴스를 생성할 수 없도록 되어 있다. 그 이유는 클래스 내에 인스턴스변수가 하나도 없어서 인스턴스를 생성할 필요가 없기 때문이다. Math클래스의 메서드는 모두 static이며, 아래와 같이 2개의 상수만 정의해 놓았다.
public static final double E = 2.7182818284590452354; //자연 로그의 밑
public static final double PI = 3.14159265358979323846; //원주율
올림, 버림, 반올림
소수점 n번째 자리에서 반올림한 값을 얻기 위해서는 round()를 사용해야 하는데, 이 메서드는 항상 소수점 첫째자리에서 반올림을 해서 정수값(long)을 결과로 돌려준다.
우리가 원하는 자리 수에서 반올림된 값을 얻기 위해서는 간다히 10의 n제곱으로 곱한 후, 다시 곱한수로 나눠주기만 하면 된다. 예를 들어 90.7552라는 값을 소수점 셋째자리에서 반올림 한 후 소수점 두 자리까지의 값을 얻고 싶으면 90.7552에 100을 곱한다. 그 결과로 9075.52가 되며 , 여기에 round()를 사용하면 그 결과는 9076이 된다. 이 결과를 다시 100.0으로 나누면 90.76이라는 값을 얻게된다.
1. 원래 값에 100을 곱한다.
90.7552 * 100 = 9075.52
2. 위의 결가에 Math.round()를 사용한다.
Math.round(9075.52)->9076
3. 위의 결과를 다시 100.0으로 나눈다.
9076/100.0 -> 90.76
9076/100 -> 90
만일 정수형 값인 100또는 100L로 나눈다면, 결과는 정수형 값을 얻게될 것이다. 위의 경우 100.0대신 100으로 나눈다면, 90이라는 정수값을 결과로 얻게된다. 정수형간의 연산에서는 반올림이 이루어지지 않는다는 것을 반드시 기억해야 한다.
소수점 넷째자리에서 반올림된 소소줌 세자리값을 얻으려면 100대신 1000을 곱하고, 1000.0으로 나누면된다. 그리고 반올림이 필요하지 않다면 round()를 사용하지 않고 단순히 1000으로 곱하고 1000.0으로 나누기만 하면 된다.
import static java.lang.Math.*;
import static java.lang.System.*;
public class MathEx1 {
public static void main(String[] args) {
double val = 90.7552;
out.println("반올림 : round("+val+")="+round(val)); //반올림
val *=100;
out.println(val);
out.println("반올림 : round("+val+")="+round(val)); //반올림
out.println("반올림 : round("+val+")/100="+round(val)/100); //반올림
out.println("반올림 : round("+val+")/100.0="+round(val)/100.0); //반올림
out.println();
out.printf("올림 : ceil(%3.1f)=%3.1f%n", 1.1,ceil(1.1)); //올림
out.printf("내림 : floor(%3.1f)=%3.1f%n",1.5,floor(1.5)); //내림
out.printf("반올림 : round(%3.1f)=%d%n",1.1,round(1.1)); // 반올림
out.printf("반올림 : round(%3.1f)=%d%n",1.5,round(1.5)); //반올림
out.printf("반올림 : rint(%3.1f)=%f%n",1.5,rint(1.5)); //반올림
out.printf("반올림 : round(%3.1f)=%d%n",-1.5,round(-1.5)); //반올림
out.printf("반올림 : rint(%3.1f)=%f%n",-1.5,rint(-1.5)); //반올림
out.printf("올림 : ceil(%3.1f)=%f%n", -1.5,ceil(-1.5)); //올림
out.printf("내림 : floor(%3.1f)=%f%n",-1.5,floor(-1.5)); //내림
}
}
==================================
반올림 : round(90.7552)=91
9075.52
반올림 : round(9075.52)=9076
반올림 : round(9075.52)/100=90
반올림 : round(9075.52)/100.0=90.76
올림 : ceil(1.1)=2.0
내림 : floor(1.5)=1.0
반올림 : round(1.1)=1
반올림 : round(1.5)=2
반올림 : rint(1.5)=2.000000
반올림 : round(-1.5)=-1
반올림 : rint(-1.5)=-2.000000
올림 : ceil(-1.5)=-1.000000
내림 : floor(-1.5)=-2.000000
Math클래스에 속한 메서드들을 활용하는 예제이다. 코드를 간단하기 위해서 정적 import문을 추가했다.
rint()도 round()처럼 소수점 첫 째자리에서 반올림하지만, 반환값이 double이다.
out.printf("반올림 : round(%3.1f)=%d%n",1.5,round(1.5)); //반환값이 int
out.printf("반올림 : rint(%3.1f)=%f%n",1.5,rint(1.5)); //반환값이 double
그리고 rint()는 두 정수의 정가운데 있는 값은 가장 가까운 짝수 정수를 반환한다.
out.printf("반올림 : round(%3.1f)=%d%n",-1.5,round(-1.5)); // -1
out.printf("반올림 : rint(%3.1f)=%f%n",-1.5,rint(-1.5)); // -2.0
round()는 소수점 첫째자리가 5일때, 더 큰 값으로 반올림하니까 -1이 되지만, rint()는 -1.5같이 홀수(-1.0)와 짝수(-2.0)의 딱 중간에 있는 경우 짝수(-2.0)을 반환한다. 그리고 음수에서는 양수와 달리 -1.5를 버림(floor)하면 -2.0이 된다.
out.printf("올림 : ceil(%3.1f)=%f%n", -1.5,ceil(-1.5)); //올림
out.printf("내림 : floor(%3.1f)=%f%n",-1.5,floor(-1.5)); //내림
'Back-end' 카테고리의 다른 글
java.lang패키지와 유용한 클래스(6) (0) | 2021.06.29 |
---|---|
java.lang패키지와 유용한 클래스(5) (0) | 2021.06.27 |
java.lang패키지와 유용한 클래스(3) (0) | 2021.06.25 |
java.lang패키지와 유용한 클래스(2) (0) | 2021.06.24 |
java.lang패키지와 유용한 클래스(1) (0) | 2021.06.23 |