문자열

String 타입을 선언하는 방법

◾ 리터럴 방식

String str = "Hello";
String str2 = "Hello";
System.out.println(str == str2);  //true

편하기 때문에 많이 사용하는 방법으로 큰따옴표(" ")로 바로 할당하는 방법이다.

이 방법은 내부적으로 JVM메모리에 있는 상수풀에 저장이 되는데 이때문에 같은 문자열을 다른 변수에 선언을 하고 ==연산을 수행하면 true가 나오는 이유가 된다. 내부적으로 상수풀에 등록된 문자열의 reference를 intern을 이용해 호출해 할당하기 때문에 같은 문자열을 참조하고 있기 때문이다.

◾ 생성자 방식

String도 class이기 때문에 생성자로 선언이 가능하고 생성자로 선언하게 되면 서로 다른 객체가 반환이되기 때문에 같은 문자열이더라도 ==연산시 false가 출력된다.

byte[] bytes = { 72, 101, 108, 108, 111 };
String s = new String(bytes);
System.out.println(s);  //Hello
String s1 = new String("Hello");
System.out.println(s == s1);  //false

String Class는 내부적으로 byte[] 형태로 문자열을 저장하고 관리하기 때문에 생성자도 byte/char 배열형태로 주면 String이 생성이 되고 생성자 안에 ""로 문자열을 입력할 수도 있다.

이때 문자열은 상수풀에서 해당 문자열을 복사하여 byte[]로 저장하게 되는데, 이는 리터럴 방식과 똑같은 방식으로 동작하기 때문에 굳이 문자열을 인수로하는 생성자 방식은 수행할 필요가 없다.

String s1 = new String("Hello").intern();
String s2 = new String("Hello");
System.out.println(s1 == s2.intern());  //true

생성자 생성 방식도 intern()함수를 이용하면 문자열 비교가 true가 될 수 있다.



특징

◾ Immutable(불변성)

내부에 선언되어있는 byte[]final로 선언이 되어있어 변경이 불가능하다.

우리가 흔히 쓰는 +=방식의 문자열을 붙이는 것은 내부적으로보면 새로운 객체를 생성해 반환하는 방식이다. 그래서 문자열을 붙이는데 StirngBuilder를 쓰는 것이 속도가 더 빠른 이유인데 StringBuilder는 내부에 관리하는 문자열이 final이 아니라 동적으로 사이즈를 관리하기 때문에 그대로 배열에 이어쓰기 때문이다.

String 객체의 문자열은 불변성이기 때문에 아래에 기술하는 대부분의 메서드들은 해당 문자열을 변환하는 것이 아니라 기준 메서드를 복사하여 변환해서 새로운 문자열을 반환하는 방식이기 때문에 모두 String형을 return하는 방식인것을 볼 수 있다.



자주 사용하는 메서드

◾ equals()

String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1 == s2);  //false
System.out.println(s1.equals(s2));  //false

==와 다르게 String객체 내부의 문자열(byte[]) 값을 비교하는 메서드이다.

public static boolean equals(byte[] value, byte[] other) {
        if (value.length == other.length) {
            for (int i = 0; i < value.length; i++) {
                if (value[i] != other[i]) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

내부함수를 보면 String이 byte배열인 점 을 이용해 한글자씩 비교하고 있는 것을 볼 수 있다.


◾ length() / isEmpty()

문자열의 길이와 문자열이 비어있는지 검사하여 반환하는 메서드

◾ toUpperCase() / toLowerCase()

문자열을 모두 대문자/소문자로 변환하는 메서드


◾ indexOf(int|String ch, (int startIdx))

String s = "Hello";
System.out.println(s.indexOf('e'));  //1
System.out.println(s.indexOf(101));  //1
System.out.println(s.indexOf("ll")); //2
System.out.println(s.indexOf('a'));  //-1

문자열 내부에서 특정 문자(char)/문자열을 찾아 시작 인덱스를 반환하는 메서드로, 두번째 매개변수로 int를 주면 해당 index부터 검사하여 결과를 반환한다.

문자/문자열이 없다면 -1을 반환한다.


◾ substring(int startIdx, (int endIdx))

String s = "Hello";
System.out.println(s.substring(0,1)); //H
System.out.println(s.substring(3));   //lo

문자열을 idx로 구분하여 자르고 자른 문자열을 반환하는 메서드로 endIdx를 지정하지 않으면 끝인덱스까지 자르게 된다.


◾ split(String regex, (int limit))

String s = "Hello Hi 안녕";
String[] array = s.split(" ");
System.out.println(Arrays.toString(array));  // [Hello, Hi, 안녕]

String[] array = s.split(" ",2);
System.out.println(Arrays.toString(array));  // [Hello, Hi 안녕]

문자열을 정규표현식을 기준으로 나누어 Stirng배열로 반환하는 메서드로 두번째 인자로 나눌 배열 개수를 지정할 수 있고 그렇게 되면 앞에서부터 limit개수까지 만 나누어 반환한다.


◾ replaceAll(String regex, String replacement)

String s = "Hello";
s = s.replaceAll("^.","h");     //hello
s = s.replaceAll("[a-z]", "a"); //aaaaa

문자열에서 정규식을 만족하는 부분들을 replacement로 바꾼 문자열을 반환하는 메서드이다.


◾ charAt(int index)

String s = "Hello";
System.out.println(s.charAt(1)); //e

//C++
string s = "Hello";
cout << s[1];

문자열을 index로 접근하고 싶을 때 사용할 수 있는 메서드로 C++에서 배열처럼 접근하는 것과 같다.


◾ toCharArray()

String s = "안녕하세요";
char[] array = s.toCharArray();
System.out.println(Arrays.toString(array));  //[안, 녕, 하, 세, 요]

문자열을 char[]로 반환하는 메서드이다.

◾ compareTo(String anotherString)

String s1 = "A";
String s2 = "B";
String s3 = "C";
String s4 = "A";
String s5 = "AA";

System.out.println(s1.compareTo(s2));  //-1
System.out.println(s1.compareTo(s3));  //-2
System.out.println(s2.compareTo(s1));  //1
System.out.println(s3.compareTo(s1));  //2
System.out.println(s1.compareTo(s4));  //0
System.out.println(s1.compareTo(s5));  //-1

두 문자열을 비교하여 사전순으로 기준 문자열이 더크면 양수, 아니면 음수, 같으면 0을 반환한다. (이때 사전순은 결국 ascii코드 값이다.)

모두 똑같다가 s1,s5와 같이 한개가 문자열의 길이가 더 길다면 문자열이 한개라도 더있는 s5가 더 크다.

  • 양수 : 기준 문자열이 더 크다.
  • 음수 : 기준 문자열이 더 작다.
  • 0 : 같다.

Comparable, Comparator를 정의하는데도 많이 사용된다.



문자열 정렬

◾ 오름차순

String s = "hello";

char[] array = s.toCharArray();
Arrays.sort(array);
s = new String(array);
System.out.println(s); //ehllo

Arrays.sort()가 기본이 오름차순정렬인걸 이용해서 toCharArray()로 char[]을 만들어 문자열을 정렬할 수 있다.


◾ 내림차순

String s = "hello";

String[] array = s.split("");
Arrays.sort(array, Collections.reverseOrder());
//Collections.reverse(Arrays.asList(array)); //위 방식과 같은 결과를 보여준다.
s = String.join("",array);
System.out.println(s);  //ollhe

문자열을 내림차순으로 정렬하고자 위 방법에서 Arrays.sort()에 Comparator속성으로 Collections.reverseOrder()를 주게 되면 array가 Object가 아닌 char[]형이라 컴파일 에러가 난다.

String형 배열을 선언해 split("")으로 각 문자마다 나누어 배열에 저장하고 정렬을 수행하고 join()을 이용해 배열을 붙여주면 문자열을 내림차순으로 정렬이 가능하다.