자바-16(문자열)

dragonappear·2021년 5월 12일
0

Java

목록 보기
16/22

학습할 것 (필수)

String 이란?
String 활용
StringBuffer,StringBuilder


String 이란?

  • 변경 불가능한(immutable) 클래스이다.
  • 문자열은 문장을 뜻한다 (ex: "JAVA" ,"A" , "Hello")
  • 문자열은 문자의 집합이므로 char 타입의 배열과 같다
  • 자바에서 문자열에 해당하는 자료형은 String이다.
char data[] = {'a', 'b', 'c'};
String data1 ="abc";
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
  • char data[] = String data1
  • String 클래스는 문자열을 저장하기 위해서 문자형 배열변수(char[] value)를 인스턴스 변수로 정의해놓고 있다.
  • 인스턴스 생성시 생성자의 매개변수로 입력되는 문자열은 이 인스턴스 변수(value)에 문자열 배열(char[])로 저장되는 것이다.
String a ="Happy day";
String b ="a";
String c = "123";
  • 위와 같이 문자열을 정의한다.
  • 하지만, String은 클래스이고 이를 사용하려면 아래와 같이 객체를 생성해야한다.
char data[] = {'a', 'b', 'c'};
String i = new String(data);
String a = new String("Happy day");
String b = new String("a");
String c = new String("123");
  • 하지만, String은 프로그램 이곳저곳 어디서나 사용하기 때문에 편의를 위해 new 연산자를 사용하지 않고 객체생성을 할 수 있다.
  • 첫번째 방식으로 표현하면 가독성에 이점이 있고 컴파일 시 최적화에 도움을 주기 때문에 첫번째 방식으로 사용하자.
  • 한번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.
  • 예를 들어, + 연산자를 이용해서 문자열을 결합하는 경우 인스턴스내의 문자열이 바뀐느 것이 아니라 새로운 문자열이 담긴 String 인스턴스가 생성되는 것이다.
  • 덧셈연사자를 사용해서 문자열을 결합하는 것은 매 연산시 마다 새로운 문자열을 가진 String 인스턴스가 생성디ㅗ어 메모리공간을 차지하게 되므로 가능한 결합횟수를 줄이는 것이 좋다.

  • String은 primitive type이 아니지만, 자바에서는 primitive type처럼 리터럴 표현식을 사용할수있게 해준다.
  • String은 메모리에 저장된 리터럴의 주소를 저장하고 있다
  • 문자열이 변경되면 기존의 객체를 버리고 새로운 객체를 메모리에 생성한다.
  • 기존 객체는 GC에 의해서 메모리 회수가 이루어진다.
  • String을 사용하여 문자열의 변경을 하면 기존객체가 GC에 의해 메모리회수가 바로 일어나는 것이 아니기 때문에 메모리의 낭비가 있고, 새로운 객체에 기존 객체를 복사하는 오버헤드도 발생한다.
  • 많은 데이터의 IO가 발생하는 극한 상황이라고 가정하면 위 문제를 고려해야한다.
  • 문자열을 다루는 작업이 많이 필요한 경우네는 String 클래스 대신 StringBuffer 클래스를 사용하는 것이 좋다. StringBuffer 인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 StringBuffer 인스턴스만으로도 문자열이 다루는것이 가능하다.

문자열 리터럴

  • 자바소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다.
  • 이 때 같은 내용의 문자열 리터럴은 한번만 저장된다.
  • 문자열 리터럴도 String 인스턴스 이고 한번 생성하면 내용을 변경할수없기 때문에 하나의 인스턴스를 공유하면 되기 때문이다.
  • String 리터럴들은 컴파일 시에 클래스파일에 저장된다.
  • 클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있다.
  • 해당 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 이 리터럴의 목록이 있는 리터럴들이 JVM 내에 있는 상수 저장소(constant pool)에 저장된다.

빈 문자열

  • 길이가 0인 배열이 존재 할 수 있다.
  • char형 배열도 길이가 0인 배열을 생성할 수있고, 이 배열을 내부적으로 가지고 있는 문자열이 바로 빈 문자열이다.
  • String s="" 와 같은 문장이 있을때, 참조변수 s가 참조하고 있는 String 인스턴스는 내부에 new Char[0]와 같이 길이가 0인 char형 배열을 저장하고 있는 것이다.
  • 그러나 String s=""과 같은 표현이 가능하다고 해서 char c='';와 같은 표현이 가능한 것은 아니다. char형 변수에는 반드시 하나의 문자를 지정해야 하낟.
  • 일반적으로 변수를 선언할때, 각 타입의 기본값으로 초기화하지만 String은 참조형 타입의 기본값인 null보다는 빈 문자열로, char형인 기본값은 ‘₩u0000’ 대신 공백으로 초기화 하는 것이 보통이다. (‘₩u0000’은 유니코드의 첫 번째 문자로써 아무런 문자도 지정되지 않은 빈 문자이다.)

String 활용:

1.문자열을 비교

compareTo

StringCompareEx

package me.whiteship.livestudy.string;

public class StringCompareEx {
    public static void main(String[] args) {
        String str ="Hello World";
        String str1 = "hfllo World";
        Object obj = str;

        System.out.println(str.compareTo(str1));
        System.out.println(str.compareToIgnoreCase(str1));
        System.out.println(str.compareTo(obj.toString()));
    }
}

output

-32
-1
0
  • 문자열 간에 아스키코드값 차이를 리턴한다. 리턴값이 0이면 같은 문자열을 가진다.

Equals

equals

package me.whiteship.livestudy.string;

public class StringEqualsEx {
    public static void main(String[] args) {
        String s1= "hello";
        String s2= "hello";
        String s3= new String("hello");
        String s4 = new String("Hello");

        System.out.println(s1.equals(s2));
        System.out.println(s1.equals(s3));
        System.out.println(s1.equals(s4));


    }
}

output

true
true
false
  • 결과로 boolean을 리턴한다. 같으면 true, 다르면 false

==

package me.whiteship.livestudy.string;

public class StringEqualsEx {
    public static void main(String[] args) {
        String s1= "hello";
        String s2= "hello";
        String s3= new String("hello");
        String s4 = new String("Hello");

        System.out.println(s1==s2);
        System.out.println(s1==s2);
        System.out.println(s1==s4);


    }
}

output

true
true
false
  • 결과로 boolean을 리턴한다. 같으면 true, 다르면 false

2. 문자열 내에서 지정된 문자열의 마지막을 검색

인스턴스.lastIndexOf(Stringname)

package me.whiteship.livestudy.string;

public class SearchLastString {
    public static void main(String[] args) {
        String str = "Hello world, Hello hi, Hello hihihi";
        int lastIndex = str.lastIndexOf("Hello");

        if(lastIndex==-1){
            System.out.println("Hello not found");
        }else{
            System.out.println(lastIndex);
            System.out.println(str.charAt(lastIndex));
        }
    }
}

output

23
H
  • lastIndexOf("Hello")는 문자열에 포함된 여러 "Hello" 들중 가장 뒤쪽에 위치한 문자열의 시작 인덱스를 리턴한다.

3. 문자열에서 지정된 문자를 삭제

  • substring()을 이용
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello World, Hello JAVA";
        System.out.println(str);
        System.out.println(str.charAt(6));
        System.out.println(removeChatAt(str,6));

    }

    public static String removeChatAt(String s, int pos){
        return s.substring(0,pos)+s.substring(pos+1);
    }
}

output

Hello World, Hello JAVA
W
Hello orld, Hello JAVA
  • deleteChatAt()을 이용
public class RemoveCharacter 
{
   public static void main(String[] args) 
   {
        StringBuilder MyString = new StringBuilder("Hello World");
        System.out.println("The string before removing character: " + MyString);
        MyString = MyString.deleteCharAt(5);
        System.out.println("The string after removing character: " + MyString);
   }
} 

output

The string before removing character: Hello World
The string after removing character: HelloWorld 

4. 문자열의 하위 문자열을 변경

replace 이용

package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello World, Hello JAVA";
        System.out.println(str.replace("H","F"));
        System.out.println(str.replaceFirst("Hello","bye"));
        System.out.println(str.replaceAll("Hello","bye"));
    }
}

output

Fello World, Fello JAVA
bye World, Hello JAVA
bye World, bye JAVA
  • replace는 문자열에 포함된 문자를 다른 문자로 replace
  • replaceFirst는 문자열에 포함된 특정 문자열 중 가장 앞에 위치한 문자열을 다른 문자열로 변경
  • replaceAll은 문자열에 포함된 특정 문자열 전체를 다른 문자열로 변경

5. 문자열 뒤집기

  • StringBuffer(String str)을 통해 입력 문자열을 버퍼링 및 반전시켜 문자열로 변환
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello World, Hello JAVA";
        String str1 = new StringBuffer(str).reverse().toString();

        System.out.println(str);
        System.out.println(str1);
    }
}

output

Hello World, Hello JAVA
AVAJ olleH ,dlroW olleH
  • 매개변수에서 문자열(str)을 반목문을 통해 거꾸로 출력
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello World, Hello JAVA";
        char[] str1 = str.toCharArray();

        for (int i = str1.length-1; i >=0; i--) {
            System.out.println(str1[i]);
        }
    }
}

output

AVAJ olleH ,dlroW olleH

6. 문자열에서 문구검색

  • 문자열에서 단어의 위치값을 반환하는 indexOf를 사용해서 단어를 검색
  • 검색결과 없을 경우 -1을 반환
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello World, Hello JAVA";
        int idx = str.indexOf("Hello");

        if(idx==-1){
            System.out.println("Hello not found");
        }else{

            System.out.println(idx);
            System.out.println(str.charAt(idx));
        }
    }
}

output

0
H
  • String 객체에서 contains를 통해 단어를 검색
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello World, Hello JAVA";
        System.out.println(str.contains("JAVA"));
    }
}

output

true

7. 문자열을 여러개의 하위 문자열로 나누기

  • split(string)을 통해 문자열 및 구분자(-,\.)로부터 여러 하위 문자열로 분할
  • 구분자가 공백의 경우 \s 를 사용
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello-World-Hello-JAVA";
        String [] tmp;
        String [] tmp1;
        String delimeter="-";
        tmp = str.split(delimeter);

        for (int i = 0; i < tmp.length; i++) {
            System.out.println(tmp[i]);
        }

        System.out.println();
        str =  "max.min.avg.sum";
        delimeter="\\.";
        tmp1=str.split(delimeter);
        for (int i = 0; i < tmp1.length; i++) {
            System.out.println(tmp1[i]);
        }
    }
}

output

Hello
World
Hello
JAVA

max
min
avg
sum

8. 문자열을 대문자로 변환

  • String 클래스의 toUpperCase를 통해 대문자로 변경
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello-World-Hello-JAVA";
        String str1 = str.toUpperCase();

        System.out.println(str);
        System.out.println(str1);
    }
}

output

Hello-World-Hello-JAVA
HELLO-WORLD-HELLO-JAVA

9. 문자열의 영역을 일치확인

  • regionMatches 를 통해 두 문자열의 영역이 일치 확인
  • 해당 전달 인자에 대한 설명
    • 1: 비교가 시작되는 문자열의 인덱스
    • 2: 대상 문자열
    • 3: 비교가 시작되는 대상문자열의 인덱스
    • 4: 비교할 문자수
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str = "Hello-World-Hello-JAVA";
        String str1 = "Hel4o-World-Hello-PYTHON";

        boolean match = str.regionMatches(str.indexOf("World"),str1,str1.indexOf("World"),3);
        System.out.println(match);
        match = str.regionMatches(str.indexOf("Hello"),str1,str1.indexOf("Hel4o"),3);
        System.out.println(match);
        match = str.regionMatches(str.indexOf("Hello"),str1,str1.indexOf("Hel4o"),4);
        System.out.println(match);
    }
}

output

true
true
false

10. 두 문자열의 생성 성능 비교

  • String literals VS String objects
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        //String str1 = "Hello-World-Hello-JAVA";
        //String str2 = "Hello-World-Hello-JAVA";

        long start = System.currentTimeMillis();
        for (int i = 0; i < 50_000; i++) {
            String str1 = "Hello-World-Hello-JAVA";
            String str2 = "Hello-World-Hello-JAVA";
        }
        long end = System.currentTimeMillis();

        System.out.println(end-start+" milli sec");


        start = System.currentTimeMillis();
        for (int i = 0; i < 50_000; i++) {
            String str3 = new String("Hello-World-Hello-JAVA");
            String str4 =  new String ("Hello-World-Hello-JAVA");
        }
        end = System.currentTimeMillis();

        System.out.println(end-start+" milli sec");


    }
}

output

2 milli sec
8 milli sec

11. 문자열 생성 최적화

  • literals와 new String 및 String.intern을 통해 문자열을 생성
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        //String str1 = "Hello-World-Hello-JAVA";
        //String str2 = "Hello-World-Hello-JAVA";

        String[] strings = new String[50_000];
        for (int i = 0; i < strings.length; i++) {
            strings[i] = "s"+i;
        }

        long start = System.currentTimeMillis();
        for (int i = 0; i < strings.length; i++) {
            strings[i] = "hello";
        }
        long end = System.currentTimeMillis();

        System.out.println(end-start + " ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < strings.length; i++) {
            strings[i] = new String("hello");
        }
        end = System.currentTimeMillis();

        System.out.println(end-start + " ms");


        start = System.currentTimeMillis();
        for (int i = 0; i < strings.length; i++) {
            strings[i] = new String("hello");
            strings[i] = strings[i].intern();
        }
        end = System.currentTimeMillis();

        System.out.println(end-start + " ms");


    }
}

output

1 ms
4 ms
9 ms

12. 문자열 포맷

  • format 이용
package me.whiteship.livestudy.string;

import java.util.Locale;

public class StringExample {
    public static void main(String[] args) {
        String str1 = "Hello-World-Hello-JAVA";
        String str2 = "Hello-World-Hello-JAVA";

        double e =Math.E;

        System.out.format("%f%n",e);
        System.out.format(Locale.GERMANY, "%-10.4f%n%n",e);

    }
}

output

2.718282
2,7183    

13. 두 줄을 연결

    • 연산자 및 StringBuffer.append 를 사용하여 문자열 연결
package me.whiteship.livestudy.string;

import java.util.Locale;

public class StringExample {
    public static void main(String[] args) {
        String str1 = "Hello-World-Hello-JAVA";
        String str2 = "Hello-World-Hello-JAVA";

        System.out.println(str1+str2);

        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(str1);
        stringBuffer.append(str2);
        System.out.println(stringBuffer);

    }
}

output

Hello-World-Hello-JAVAHello-World-Hello-JAVA
Hello-World-Hello-JAVAHello-World-Hello-JAVA

14. 문자열 유니코드

  • codePointAt 을 이용하여 지정된 인덱스에 해당하는 문자의 유니코드를 구한다.
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str1 = "Hello-World-Hello-JAVA";
        String str2 = "Hello-World-Hello-JAVA";

        System.out.println(str1.codePointAt(0));
    }
}

output

72

15. 문자열 공백없애기

  • trim 을 이용하여 문자열의 시작과 끝에 있는 공백을 없애준다. 중간 공백은 없애주지 않는다.

String str = "    h E L L O  ";
System.out.println(str.trim());

output

h E L L O

16. 문자열 합치기

  • concat() 을 이용하여 문자열을 이어준다
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str1 = "Hello-World-Hello-JAVA123";
        String str2 = "Hello-World-Hello-JAVA";

        System.out.println(str1.concat(str2));
    }
}

output

Hello-World-Hello-JAVA123Hello-World-Hello-JAVA

17. 문자열 중 특정문자열 추출

  • substring을 이용
package me.whiteship.livestudy.string;

public class StringExample {
    public static void main(String[] args) {
        String str1 = "Hello-World-Hello-JAVA123";
        String str2 = "Hello-World-Hello-JAVA";

        System.out.println(str1.substring(0,5));
        System.out.println(str1.substring(6));
    }
}

output

Hello
World-Hello-JAVA123

18. 문자 인코딩 변환

  • getBytes(String charsetName)을 사용하면, 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있다.
  • 자바가 UTF-16을 사용하지만. 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다.

byte[] str = "가".getBytes("UTF-8"); // 문자열을 UTF-8로 변환
String str = new String(str, "UTF-8"); // byte 배열을 문자열로 변환
  • 서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 문자 인코딩이 필요하다.
  • UTF-8은 한글 한 글자를 3byte로 표현하고 , CP249는 2byte로 표현한다.

19. 기본형을 String으로 변환

  • 기본형을 문자열로 변경하는 방법은 간단하다.
  • 숫자에 빈 문자열""을 더해주기만 하면된다.
  • 이 외에도 valueOf()을 사용하는 방법도 있다.
  • 성능은 valueOf()가 더 좋지만, 빈 문자열을 더하는 방법이 간단하고 편하기 때문에 성능향상이 필요한 경우메나 valueOf()를 써보자.
int i = 10;
String s= String.valueOf(i);
System.out.println(s);
        
int i = 10;
String s = i+"";
System.out.println(s);

20. String을 기본형 값으로 변환

  • 이전에는 parseInt()와 같은 메서드를 많이 사용했었는데, 메서드의 이름을 통일하기 위해 valueOf() 메서드가 나중에 추가되었다.
  • valueOf(String s) 는 메서드 내부에서 그저 parseInt(String s)를 호출한다.
   public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }
  • 두 메서드는 반환타입만 다르지 같은 메서드이다.
        String s=  "10";
        int i = Integer.valueOf(s);
        System.out.println(i);

StringBuffer,StringBuilder

  • String 클래스의 단점을 보완하기 위해 나온 클래스들이다.
  • String 클래스는 인스턴스를 생성할때 지정한 문자열을 변경할 수 없지만 StringBuffer 클래스는 변경이 가능하다.
  • 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer 인스턴스를 생성할때 그 크기를 지정할수있다.
public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
	 private transient char[] toStringCache;
  • StringBuffer 클래스는 String 클래스와 같이 문자열을 저장하기 위한 char형 배열의 참조변수 인스턴스로 선언해 놓고 있다.
  • StringBuffer 인스턴스가 생성될때,char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스변수 value가 참조하게 된다.
  • String과 달리 데이터가 변경되어도 기존 객체를 재활용하기 때문에 데이터 변경면에서는 String보다 빠르다.
  • 대체적으로 String과 매우 흡사하다.
  • 극한 상황이 아니라면 아무거나 사용해도 상관없다.

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);
}

public StringBuffer(String str) {
    this(str.length() + 16);
    append(str);
}
  • StringBuffer 인스턴스로 문자열을 다루는 작업을 할때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다.

  • 배열의 길이는 변경될수없으므로 새로운 길이의 배열을 생성한후에 이전 배열의 값을 복사해야한다

StringBuffer의 비교

  • 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 인스턴스에 toSting()을 호출하면, 담고잇는 문자열을 String으로 변환한다.
  • 그래서 StringBuffer 인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer 인스턴스에 toString()을 호출해서 String 인스턴스를 얻은 다음에, 여기에 equals 메서드를 사용해서 비교해야 한다.

String Builder란?

  • StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어있다.
  • 멀티쓰레드로 작성된 프로그램이 아닌경우, StringBuffer의 동기화는 불필요하게 성능만 떨어뜨리게 된다.
  • 그래서 StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.

참고

참고링크
참고링크
★★참고링크


0개의 댓글