High Level Programming Language/Learning the Java Language

Lesson: Numbers and Strings [Strings]

노마드개발자 2024. 6. 5. 14:21

Strings

Java 프로그래밍에서 널리 사용되는 문자열은 일련의 문자입니다. Java 프로그래밍 언어에서 문자열은 객체입니다.

Java 플랫폼은 문자열을 생성하고 조작하기 위한 String 클래스를 제공합니다.


public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    // 내부적으로 문자를 저장하는 char 배열
    private final char value[];

    // String 클래스의 생성자 예시
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

    // 기타 메서드와 구현...
}

 

문자열 리터럴은 컴파일 시에 만들어지고, 실행 시에 자바의 문자열 상수 풀(String Constant Pool)에 저장됩니다. 문자열 리터럴은 소스 코드에서 직접 정의된 문자열을 의미하며, 컴파일러는 이러한 리터럴을 문자열 상수 풀에 저장하여 메모리 사용을 최적화합니다. 

문자열 리터럴 생성 시점:
1. 컴파일 시:

  • 소스 코드에 있는 문자열 리터럴은 컴파일 시에 클래스 파일에 저장됩니다.
  • 컴파일러는 소스 코드의 모든 문자열 리터럴을 찾아 문자열 상수 풀에 저장할 준비를 합니다.

2. 클래스 로딩 시:

  • 프로그램이 실행될 때, JVM이 클래스를 로드하면서 문자열 리터럴을 문자열 상수 풀에 저장합니다.
  • 이 과정에서 동일한 문자열 리터럴이 여러 번 사용되면, 동일한 객체를 재사용하여 메모리 낭비를 줄입니다.

 

What is the string pool?

string pool은 문자열 리터럴이 저장되는 자바 Heap의 저장 영역일 뿐입니다. 문자열 인턴 풀(String Intern Pool) 또는 문자열 상수 풀(String Constant Pool)이라고도 합니다. 객체 할당과 비슷합니다. 기본적으로는 비어 있으며 자바 String 클래스에 의해 private으로 관리됩니다. 문자열을 생성할 때마다 문자열 객체는 힙 메모리의 일부 공간을 차지합니다. 다수의 문자열을 생성하면 비용과 메모리가 증가할 수 있으며 이는 성능 저하로 이어질 수 있습니다.

 

JVM은 문자열 리터럴 초기화 동안 성능을 향상시키고 메모리 부담을 줄이기 위해 몇 가지 단계를 수행합니다. JVM에서 생성되는 문자열 객체의 수를 줄이기 위해 String 클래스는 문자열 풀을 유지합니다.

 

문자열 리터럴을 생성할 때, JVM은 먼저 문자열 풀이 해당 리터럴을 가지고 있는지 확인합니다. 만약 문자열 풀이 이미 해당 리터럴을 가지고 있다면, 풀에 있는 인스턴스에 대한 참조를 반환합니다. 문자열 풀이 해당 리터럴을 가지고 있지 않다면, 새로운 String 객체가 문자열 풀에 생성됩니다.

 

Creating String in Java

Java에서 문자열을 만드는 방법에는 두 가지가 있습니다.

 

Using String Literal

String str1 = "Python";

String str2 = "Data Science";

String str3 = "Python";

 

Using new Keyword

Java에서는 다음과 같이 문자열을 생성하는 데 new 키워드도 사용됩니다.

String str4 = new String ("Java");

String str5 = new String ("C++");

String str6 = new String ("Data Science");

String str7 = new String("Data Science").intern();

 

위 코드는 다음과 같이 Java Heap에서 스트링 리터럴을 생성합니다.

 

먼저, 우리는 문자열 리터럴 "Python"을 생성했고, 이는 문자열 풀에 저장됩니다. 그 후에 문자열 "Data Science"를 생성했으며, 이 또한 문자열 풀에 저장됩니다. 마지막으로 다시 문자열 "Python"을 생성했습니다. 그러나 이번에는 JVM이 해당 문자열을 확인하고, 이미 문자열 풀이 해당 리터럴을 가지고 있음을 발견합니다. 따라서 새로운 인스턴스를 생성하는 대신, 문자열 풀에 있는 인스턴스의 참조를 반환합니다. 즉, str1의 참조를 반환합니다.

 

유사하게, new 키워드를 사용하여 문자열 리터럴을 생성할 때도 문자열 풀에 저장됩니다. 우리는 "Java", "C++", 그리고 "Data Science"라는 세 개의 문자열 리터럴을 생성했습니다. "Java"와 "C++" 문자열 리터럴은 새로운 것이지만, "Data Science" 리터럴은 이미 풀에 존재합니다. 이 시점에서 JVM은 Java 힙에 "Data Science" 리터럴을 위한 공간을 할당합니다. new 키워드로 생성된 모든 문자열 리터럴은 문자열 풀에 저장되지 않고 Java 힙에 저장된다는 점을 기억하세요.

 

Java String.intern() MethodString.intern() 메서드는 문자열을 문자열 풀에 넣거나 동일한 값을 가진 문자열 풀의 다른 문자열 객체를 참조합니다. 문자열 풀이 이미 해당 문자열 객체와 동일한 문자열을 포함하고 있는 경우, 풀에서 문자열을 반환합니다. 이 문자열은 String.equals(Object) 메서드를 사용하여 결정됩니다. 문자열이 이미 존재하지 않는 경우, 문자열 객체가 풀에 추가되고 이 문자열 객체에 대한 참조가 반환됩니다.

 

두 문자열 str1과 str2에 대해, str1.intern() == str2.intern()이 true가 되려면, str1.equals(str2)가 true여야만 합니다.

str1.intern() == str2.intern()이 true를 반환한다는 것은 str1.equals(str2)가 true를 반환한다는 것과 같은 의미입니다. 이것은 두 문자열이 동일한 내용을 가지며, 동일한 문자열 풀 객체를 참조하고 있음을 의미합니다.

※ 자바에서 모든 문자열 리터럴과 문자열 값을 가지는 상수 표현식은 자동으로 interned(인터닝) 됩니다. 이는 메모리 사용의 효율성을 높이고 동일한 문자열 리터럴이 여러 번 사용될 때 메모리 낭비를 줄이기 위함입니다.

인터닝(Interning)은 자바에서 문자열을 효율적으로 관리하기 위한 기술입니다. 문자열을 인턴(intern)한다는 것은 동일한 내용의 문자열이 여러 개 존재하지 않도록 문자열을 공유하여 메모리 사용을 최적화하는 것을 의미합니다

 

public String intern()

 

intern 메서드는 String 객체의 표준 표현( canonical representation )을 리턴합니다.

자바에서 intern() 메서드가 반환하는 "표준 표현(canonical representation)"이란 문자열 상수 풀(String Constant Pool) 내에서 해당 문자열의 유일한 참조를 의미합니다. 표준 표현은 동일한 문자열 리터럴이 여러 번 사용될 때 하나의 객체만 사용되도록 하여 메모리 사용을 최적화하고, 문자열 비교를 빠르게 수행할 수 있게 합니다.

 

예를 들면,

String str1 = new String ("Javatpoint");

 

위의 statement은 Java 힙에 문자열을 생성합니다. 그렇다면 다음 statement와는 어떤 차이점이 있는 것일까요?

str1.intern(); or String s1 = new String("Javatpoint").intern();

 

두 가지 문자열 초기화 방식의 차이점은 intern() 메서드를 사용했는지 여부와 이에 따른 문자열 객체의 위치와 메모리 사용 방식입니다.

차이점 설명:
1. String str1 = new String("Javapoint");:
  이 구문은 두 개의 문자열 객체를 생성합니다.
  첫 번째는 문자열 상수 풀(String Constant Pool)에 "Javapoint" 리터럴을 저장합니다.
  두 번째는 new String("Javapoint")를 통해 힙 메모리에 새로운 String 객체를 생성합니다.
  따라서, str1은 힙 메모리에 있는 새로운 String 객체를 참조합니다.

2. String str1 = new String("Javapoint").intern();:
  이 구문은 먼저 문자열 상수 풀에 "Javapoint" 리터럴을 저장하고, 힙 메모리에 새로운 String 객체를 생성합니다.
  그런 다음, intern() 메서드를 호출하여 문자열 상수 풀에서 "Javapoint" 리터럴에 대한 참조를 반환합니다.
  따라서, str1은 힙 메모리에 있는 새로운 String 객체 대신 문자열 상수 풀에 있는 "Javapoint" 객체를 참조합니다.

 

public class StringInternExample  
{  
    public static void main(String args[])   
    {  
        //it will store in the string pool      
        String str1 = "Python";  
        String str2 = "Data Science";  
        //it returns the reference of the pooled instance i.e. str1  
        //it will not take place in the string pool  
        String str3 = "Python";  
        String str4 = "C";  
        //store in Java heap  
        String str5 = new String ("Java");  
        //store in Java heap  
        String str6 = new String ("C++");  
        //store in Java heap  
        String str7 = new String ("Data Science");  
        //it will not take place in Java heap  
        //it will store in string pool  
        String str8 = new String ("C").intern();  
        //returns false  
        System.out.println((str1 == str5)+", Strings are not equal.");  
        //returns false because str2 occupies space in string pool and str7 occupies space in Java heap  
        System.out.println((str2 == str7)+", Strings are not equal.");   
        //it returns true because we have invoked the intern() method and the String C is already present in the string pool  
        System.out.println((str4 == str8)+", Strings are equal.");   
        }  
}

 

Creating Strings

문자열을 생성하는 가장 직접적인 방법은 다음과 같이 작성하는 것입니다.

String greeting = "Hello world!";

 

이 경우에는 "Hello world!" 문자열 리터럴(큰따옴표로 묶인 코드의 일련의 문자)입니다. 코드에서 문자열 리터럴을 발견할 때마다 컴파일러는 해당 값(이 경우 Hello world!)을 사용하여 String 객체를 만듭니다.

 

다른 객체와 마찬가지로 new 키워드와 생성자를 사용하여 String 개체를 만들 수 있습니다. String 클래스에는 문자 배열과 같은 다양한 소스를 사용하여 문자열의 초기 값을 제공할 수 있는 13개의 생성자가 있습니다.

char[] helloArray = { 'h', 'e', 'l', 'l', 'o', '.' };
String helloString = new String(helloArray);
System.out.println(helloString);

 

이 코드 조각의 마지막 라인은 hello 문자열을 출력합니다.


참고: String 클래스는 변경할 수 없으므로 일단 생성되면 String 객체를 변경할 수 없습니다. String 클래스에는 문자열을 수정하는 것으로 보이는 여러 메서드가 있으며 그 중 일부는 아래에서 설명합니다. 문자열은 변경할 수 없기 때문에 이러한 메서드가 실제로 수행하는 작업은 작업 결과가 포함된 새 문자열을 생성하고 반환하는 것입니다.


 

String Length

객체에 대한 정보를 얻는 데 사용되는 메서드를 접근자[accessor] 메서드라고 합니다. 문자열과 함께 사용할 수 있는 접근자 메서드 중 하나는 문자열 객체에 포함된 문자 개수를 반환하는 length() 메서드입니다. 다음 두 라인의 코드가 실행된 후 len은 17과 같습니다.

String palindrome = "Dot saw I was Tod";
int len = palindrome.length();

 

회문[palindrome]은 대칭적인 단어나 문장입니다. 즉, 대소문자와 구두점을 무시하고 앞뒤로 동일하게 철자가 작성됩니다. 다음은 회문 문자열을 뒤집는 짧고 비효율적인 프로그램입니다. 0부터 계산하여 문자열의 i번째 문자를 반환하는 문자열 메서드 charAt(i)를 호출합니다.

public class StringDemo {
    public static void main(String[] args) {
        String palindrome = "Dot saw I was Tod";
        int len = palindrome.length();
        char[] tempCharArray = new char[len];
        char[] charArray = new char[len];
        
        // put original string in an 
        // array of chars
        for (int i = 0; i < len; i++) {
            tempCharArray[i] = 
                palindrome.charAt(i);
        } 
        
        // reverse array of chars
        for (int j = 0; j < len; j++) {
            charArray[j] =
                tempCharArray[len - 1 - j];
        }
        
        String reversePalindrome =
            new String(charArray);
        System.out.println(reversePalindrome);
    }
}

 

이 프로그램을 실행시키면, 다음과 같은 출력이 만들어진다.

doT saw I was toD

 

문자열 반전을 수행하기 위해 프로그램은 문자열을 문자 배열(첫 번째 for 루프)로 변환하고, 배열을 두 번째 배열(두 번째 for 루프)로 반전시킨 다음 다시 문자열로 변환해야 했습니다. String 클래스에는 문자열 또는 문자열의 일부를 문자 배열로 변환하는 getChars() 메서드가 포함되어 있으므로 위 프로그램의 첫 번째 for 루프를 다음으로 바꿀 수 있습니다.

palindrome.getChars(0, len, tempCharArray, 0);

 

Concatenation Strings

String 클래스에는 두 문자열을 연결하는 메서드가 포함되어 있습니다.

string1.concat(string2);

 

그러면 끝에 string2가 추가된 string1인 새 문자열이 반환됩니다.

다음과 같이 문자열 리터럴과 함께 concat() 메서드를 사용할 수도 있습니다.

"My name is ".concat("Rumplestiltskin");

 

문자열은 다음과 같이 + 연산자와 더 일반적으로 연결됩니다.

"Hello," + " world" + "!"

 

그 결과,

"Hello, world!"

 

+ 연산자는 print 문에서 널리 사용됩니다. 예를 들어:

String string1 = "saw I was ";
System.out.println("Dot " + string1 + "Tod");

 

위 코드는 다음과 같이 출력한다.

Dot saw I was Tod

 

이러한 연결은 모든 객체의 혼합일 수 있습니다. 문자열이 아닌 각 객체의 경우 해당 객체의 toString() 메서드가 호출되어 이를 문자열로 변환합니다.


참고: Java 프로그래밍 언어에서는 리터럴 문자열이 소스 파일의 라인들에 걸쳐 있는 것을 허용하지 않으므로 여러 줄 문자열의 각 줄 끝에 + 연결 연산자를 사용해야 합니다. 예를 들어:

String quote = 
    "Now is the time for all good " +
    "men to come to the aid of their country.";

 

+ 연결 연산자를 사용하여 라인들 사이에서 문자열을 나누는 것은 다시 한번 print 문에서 매우 일반적입니다.


 

Creating Format Strings

포맷이 지정된 숫자로 출력을 프린트하기 위해 printf() 및 format() 메서드를 사용하는 방법을 살펴보았습니다. String 클래스에는 PrintStream 객체가 아닌 String 객체를 반환하는 동등한 클래스 메서드인 format()이 있습니다.

String의 정적 format() 메서드를 사용하면 일회성 print 문과 달리 재사용할 수 있는 포맷화된 문자열을 만들 수 있습니다. 예를 들어, 

System.out.printf("The value of the float " +
                  "variable is %f, while " +
                  "the value of the " + 
                  "integer variable is %d, " +
                  "and the string is %s", 
                  floatVar, intVar, stringVar);

 

대신

String fs;
fs = String.format("The value of the float " +
                   "variable is %f, while " +
                   "the value of the " + 
                   "integer variable is %d, " +
                   " and the string is %s",
                   floatVar, intVar, stringVar);
System.out.println(fs);

 

위 코드를 쓸 수 있습니다

 

Converting Between Numbers and Strings

Converting Strings to Numbers

프로그램이 문자열 객체의 숫자 데이터(예: 사용자가 입력한 값)로 끝나는 경우가 많습니다.

 

기본 숫자 유형을 래핑하는 Number 하위 클래스 (Byte, Integer, Double, Float, Long 및 Short)는 각각 문자열을 해당 유형의 객체로 변환하는 valueOf라는 클래스 메서드를 제공합니다. 다음은 커맨드라인에서 두 개의 문자열을 가져와 숫자로 변환하고 해당 값에 대해 산술 연산을 수행하는 ValueOfDemo 예제입니다.

public class ValueOfDemo {
    public static void main(String[] args) {

        // this program requires two 
        // arguments on the command line 
        if (args.length == 2) {
            // convert strings to numbers
            float a = (Float.valueOf(args[0])).floatValue(); 
            float b = (Float.valueOf(args[1])).floatValue();

            // do some arithmetic
            System.out.println("a + b = " +
                               (a + b));
            System.out.println("a - b = " +
                               (a - b));
            System.out.println("a * b = " +
                               (a * b));
            System.out.println("a / b = " +
                               (a / b));
            System.out.println("a % b = " +
                               (a % b));
        } else {
            System.out.println("This program " +
                "requires two command-line arguments.");
        }
    }
}

 

다음은 커맨드라인 아규먼트로 4.5 및 87.2를 사용할 때 프로그램의 출력입니다.

a + b = 91.7
a - b = -82.7
a * b = 392.4
a / b = 0.0516055
a % b = 4.5

 


참고: 기본 숫자 타입을 래핑하는 각 Number 하위 클래스는 문자열을 기본 숫자로 변환하는 데 사용할 수 있는parseXXXX() 메서드(예:parseFloat())도 제공합니다. 객체 대신 기본 타입이 반환되므로,parseFloat() 메서드는 valueOf() 메서드보다 더 직접적입니다. 예를 들어 ValueOfDemo 프로그램에서는 다음을 사용할 수 있습니다.

float a = Float.parseFloat(args[0]);
float b = Float.parseFloat(args[1]);

Converting Numbers to Strings

문자열 포맷의 값에 대해 작업을 수행해야 하기 때문에 숫자를 문자열로 변환해야 하는 경우가 있습니다. 숫자를 문자열로 변환하는 몇 가지 쉬운 방법이 있습니다.

int i;
// Concatenate "i" with an empty string; conversion is handled for you.
String s1 = "" + i;

 

또는,

// The valueOf class method.
String s2 = String.valueOf(i);

 

각 Number 하위 클래스에는 기본 유형을 문자열로 변환하는 클래스 메서드 toString()이 포함되어 있습니다. 예를 들어:

int i;
double d;
String s3 = Integer.toString(i); 
String s4 = Double.toString(d);

 

ToStringDemo 예제에서는 toString 클래스 메서드를 사용하여 숫자를 문자열로 변환합니다. 그런 다음 프로그램은 몇 가지 String 메서드를 사용하여 소수점 앞과 뒤의 자릿수를 계산합니다.

public class ToStringDemo {
    
    public static void main(String[] args) {
        double d = 858.48;
        String s = Double.toString(d);
        
        int dot = s.indexOf('.');
        
        System.out.println(dot + " digits " +
            "before decimal point.");
        System.out.println( (s.length() - dot - 1) +
            " digits after decimal point.");
    }
}

 

이 프로그램의 출력은:

3 digits before decimal point.
2 digits after decimal point.

 

Manipulating Characters in a String

String 클래스에는 문자열의 내용을 검사하고, 문자열 내에서 문자나 하위 문자열을 찾고, 대소문자를 변경하고, 기타 작업을 수행하기 위한 다양한 메서드가 있습니다.

 

Getting Characters and Substrings by Index

charAt() 접근자 메서드를 호출하여 문자열 내의 특정 인덱스에 있는 문자를 가져올 수 있습니다. 첫 번째 문자의 인덱스는 0이고 마지막 문자의 인덱스는 length()-1입니다. 예를 들어, 다음 코드는 문자열의 인덱스 9에 있는 문자를 가져옵니다.

String anotherPalindrome = "Niagara. O roar again!"; 
char aChar = anotherPalindrome.charAt(9);

 

인덱스는 0에서 시작하므로 다음 그림에 표시된 대로 인덱스 9의 문자는 'O'입니다.

 

문자열에서 둘 이상의 연속 문자를 얻으려면 substring 메서드을 사용할 수 있습니다. substring 메서드에는 다음 테이블에 표시된 대로 두 가지 버전이 있습니다.

 

The substring Methods in the String Class

Method Description
String substring(int beginIndex, int endIndex) 이 문자열의 하위 문자열인 새 문자열을 반환합니다. 하위 문자열은 지정된 BeginIndex에서 시작하여 인덱스 endIndex - 1의 문자까지 확장됩니다.
String substring(int beginIndex) 이 문자열의 하위 문자열인 새 문자열을 반환합니다. 정수 아규먼트는 첫 번째 문자의 인덱스를 지정합니다. 여기서 반환된 하위 문자열은 원래 문자열의 끝까지 확장됩니다.

 

다음 코드는 나이아가라 회문에서 인덱스 11부터 "roar"라는 단어인 인덱스 15까지(포함하지 않음) 확장되는 하위 문자열을 가져옵니다.

String anotherPalindrome = "Niagara. O roar again!"; 
String roar = anotherPalindrome.substring(11, 15);

 

 

 

Other Methods for Manipulating Strings

문자열을 조작하기 위한 몇 가지 다른 String 메서드는 다음과 같습니다.

 

Other Methods in the String Class for Manipulating Strings

Method Description
String[] split(String regex)
String[] split(String regex, int limit)
문자열 아규먼트(정규 표현식 포함)로 지정된 일치 항목을 검색하고 이에 따라 이 문자열을 문자열 배열로 분할합니다. 선택적 정수 아규먼트는 리턴된 배열의 최대 크기를 지정합니다. 정규식은 "정규식" 단원에서 다룹니다.
CharSequence subSequence(int beginIndex, int endIndex) beginIndex 인덱스부터 endIndex - 1까지 생성된 새 문자 시퀀스를 반환합니다.
String trim() 이 문자열의 앞뒤 공백이 제거된 복사본을 리턴합니다.
String toLowerCase()
String toUpperCase()
소문자 또는 대문자로 변환된 이 문자열의 복사본을 리턴합니다. 변환이 필요없는 경우 이러한 메서드는 원래 문자열을 리턴합니다.

 

Searching for Characters and Substrings in a String

다음은 문자열 내에서 문자나 하위 문자열을 찾는 String 메서드들입니다. String 클래스는 특정 문자 또는 하위 문자열의 문자열 내 위치를 리턴하는 접근자 메서드인 indexOf() 및 lastIndexOf()를 제공합니다. indexOf() 메서드는 문자열의 처음부터 앞으로 검색하고, lastIndexOf() 메서드는 문자열 끝에서 뒤로 검색합니다. 문자나 부분 문자열을 찾을 수 없으면 indexOf() 및 lastIndexOf()는 -1을 리턴합니다.

String 클래스는 문자열에 특정 문자 시퀀스가 ​​포함된 경우 true를 리턴하는 검색 메서드인 contain도 제공합니다. 문자열에 특정 문자 시퀀스가 ​​포함되어 있다는 것만 알면 되는, 정확한 위치는 중요하지 않은 경우 이 방법을 사용하세요.

 

다음 테이블에서는 다양한 문자열 검색 방법을 설명합니다.

The Search Methods in the String Class

Method Description
int indexOf(int ch)
int lastIndexOf(int ch)
지정된 문자가 처음(마지막) 나타나는 인덱스를 리턴합니다.
int indexOf(int ch, int fromIndex)
int lastIndexOf(int ch, int fromIndex)
지정된 인덱스에서 앞으로(뒤로) 검색하여 지정된 문자가 처음(마지막) 나타나는 인덱스를 리턴합니다.
int indexOf(String str)
int lastIndexOf(String str)
지정된 하위 문자열이 처음(마지막) 나타나는 인덱스를 리턴합니다.
int indexOf(String str, int fromIndex)
int lastIndexOf(String str, int fromIndex)
지정된 인덱스에서 앞으로(뒤로) 검색하여 지정된 하위 문자열이 처음(마지막) 나타나는 인덱스를 리턴합니다.
boolean contains(CharSequence s) 문자열에 지정된 문자 시퀀스가 ​​포함되어 있으면 true를 리턴합니다.

참고: CharSequence는 String 클래스에 의해 구현되는 인터페이스입니다. 따라서 string을 contain() 메서드의 아규먼로 사용할 수 있습니다.


 

Replacing Characters and Substrings into a String

String 클래스에는 문자열에 문자나 하위 문자열을 삽입하는 메서드가 거의 없습니다. 일반적으로 필요하지 않습니다. 삽입하려는 하위 문자열과 문자열에서 제거한 하위 문자열을 연결하여 새 문자열을 생성할 수 있습니다.

그러나 String 클래스에는 발견된 문자나 하위 문자열을 바꾸는 네 가지 메서드가 있습니다. 그들은:

 

Methods in the String Class for Manipulating Strings

Method Description
String replace(char oldChar, char newChar) 이 문자열에서 oldChar를 모두 newChar로 대체한 결과로 생성된 새 문자열을 반환합니다.
String replace(CharSequence target, CharSequence replacement) 리터럴 타겟 시퀀스와 일치하는 이 문자열의 각 하위 문자열을 지정된 리터럴 대체 시퀀스로 바꿉니다.
String replaceAll(String regex, String replacement) 주어진 정규식과 일치하는 이 문자열의 각 부분 문자열을 주어진 대체 문자열로 바꿉니다.
String replaceFirst(String regex, String replacement) 주어진 정규식과 일치하는 이 문자열의 첫 번째 부분 문자열을 주어진 대체 문자열로 바꿉니다.

 

An Example

다음 클래스 Filename은 lastIndexOf() 및 substring()을 사용하여 파일 이름의 여러 부분을 분리하는 방법을 보여줍니다.


참고: 다음 Filename 클래스의 메서드는 오류 검사를 수행하지 않으며 아규먼트에 전체 디렉터리 경로와 확장명이 포함된 파일 이름이 포함되어 있다고 가정합니다. 이러한 메서드가 프로덕션 코드인 경우 아규먼트가 올바르게 구성되었는지 확인합니다.


 

public class Filename {
    private String fullPath;
    private char pathSeparator, 
                 extensionSeparator;

    public Filename(String str, char sep, char ext) {
        fullPath = str;
        pathSeparator = sep;
        extensionSeparator = ext;
    }

    public String extension() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        return fullPath.substring(dot + 1);
    }

    // gets filename without extension
    public String filename() {
        int dot = fullPath.lastIndexOf(extensionSeparator);
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(sep + 1, dot);
    }

    public String path() {
        int sep = fullPath.lastIndexOf(pathSeparator);
        return fullPath.substring(0, sep);
    }
}

 

다음은 Filename 객체를 구성하고 해당 메서드를 모두 호출하는 FilenameDemo 프로그램입니다.

public class FilenameDemo {
    public static void main(String[] args) {
        final String FPATH = "/home/user/index.html";
        Filename myHomePage = new Filename(FPATH, '/', '.');
        System.out.println("Extension = " + myHomePage.extension());
        System.out.println("Filename = " + myHomePage.filename());
        System.out.println("Path = " + myHomePage.path());
    }
}

 

그리고 프로그램의 출력은 다음과 같습니다.

Extension = html
Filename = index
Path = /home/user

 

다음 그림에서 볼 수 있듯이 extension 메서드는 lastIndexOf를 사용하여 파일 이름에서 마지막 마침표(.)를 찾습니다. 그런 다음 하위 문자열은 lastIndexOf의 반환 값을 사용하여 파일 이름 확장명, 즉 마침표부터 문자열 끝까지의 하위 문자열을 추출합니다. 이 코드는 파일 이름에 마침표가 있다고 가정합니다. 파일 이름에 마침표가 없으면 lastIndexOf는 -1을 반환하고 하위 문자열 메서드는 StringIndexOutOfBoundsException을 발생시킵니다.

 

또한, extension 메서드가 substring의 아규먼트로 dot + 1을 사용하는 것에 주목하십시오. 만약 마침표 문자 (.)가 문자열의 마지막 문자라면, dot + 1은 문자열의 길이와 같아지며, 이는 인덱스가 0부터 시작하기 때문에 문자열의 가장 큰 인덱스보다 하나 더 큰 값이 됩니다. substring 메서드는 아규먼트가 문자열의 길이와 같을 수는 있지만, 이를 초과할 수는 없으므로 이것은 합법적인 인수입니다. 이 경우, substring 메서드는 "문자열의 끝"을 의미하는 것으로 해석합니다.

 

 

Comparing Strings and Portions of Strings

String 클래스에는 문자열 및 문자열의 일부를 비교하는 여러 메서드가 있습니다. 다음 표는 이러한 메서드들을 나열합니다.

 

Methods for Comparing Strings

Method Description
boolean endsWith(String suffix)
boolean startsWith(String prefix)
이 문자열이 이 메서드의 아규먼트로 전달된 하위 문자열로 끝나거나 시작하는 경우 true를 반환합니다.
boolean startsWith(String prefix, int offset) offset 인텍스에서 시작하는 문자열을 고려하고 아규먼트로 전달된 하위 문자열로 시작하면 true를 반환합니다.
int compareTo(String anotherString) 두 문자열을 사전순으로 비교합니다. 이 문자열이 전달된 아규먼트보다 큰지(결과는 > 0), 같은지(결과는 = 0), 작은지(결과는 < 0) 여부를 나타내는 정수를 반환합니다.
int compareToIgnoreCase(String str) 대소문자 차이를 무시하고 두 문자열을 사전순으로 비교합니다. 이 문자열이 전달된 아규먼트보다 큰지(결과는 > 0), 같은지(결과는 = 0), 작은지(결과는 < 0) 여부를 나타내는 정수를 반환합니다.
boolean equals(Object anObject) 전달된 아규먼트가 이 객체와 동일한 문자 시퀀스를 나타내는 String 객체인 경우에만 true를 반환합니다.
boolean equalsIgnoreCase(String anotherString) 전달된 아규먼트가 이 객체와 동일한 문자 시퀀스를 나타내는 String 객체인 경우에만 true를 반환하고 대소문자 차이는 무시합니다.
boolean regionMatches(int toffset, String other, int ooffset, int len) 이 문자열의 지정된 영역이 전달된 String 아규먼트(other)의 지정된 영역과 일치하는지 테스트합니다.
영역의 길이는 len이고 이 문자열의 경우 인덱스 tooffset에서 시작하고 전달된 아규먼트 문자열의 경우 ooffset에서 시작됩니다.
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 이 문자열의 지정된 영역이 전달된 String 아규먼트(other)의 지정된 영역과 일치하는지 테스트합니다.
영역의 길이는 len이고 이 문자열의 경우 인덱스 tooffset에서 시작하고 전달된 아규먼트 문자열의 경우 ooffset에서 시작됩니다.

부울 인수는 대소문자를 무시해야 하는지 여부를 나타냅니다. true인 경우 문자를 비교할 때 대소문자가 무시됩니다.
boolean matches(String regex) 이 문자열이 지정된 정규식(regex 아규먼트)과 일치하는지 테스트합니다. 정규식은 "정규식" 단원에서 논의됩니다.

 

다음 프로그램인 RegionMatchesDemo는 RegionMatches 메서드를 사용하여 다른 문자열 내에서 문자열을 검색합니다.

public class RegionMatchesDemo {
    public static void main(String[] args) {
        String searchMe = "Green Eggs and Ham";
        String findMe = "Eggs";
        int searchMeLength = searchMe.length();
        int findMeLength = findMe.length();
        boolean foundIt = false;
        for (int i = 0; 
             i <= (searchMeLength - findMeLength);
             i++) {
           if (searchMe.regionMatches(i, findMe, 0, findMeLength)) {
              foundIt = true;
              System.out.println(searchMe.substring(i, i + findMeLength));
              break;
           }
        }
        if (!foundIt)
            System.out.println("No match found.");
    }
}

 

이 프로그램의 출력은 Eggs 입니다.


프로그램은 searchMe가 참조하는 문자열을 한 번에 한 문자씩 살펴봅니다. 각 문자에 대해 프로그램은 지역 일치 메서드를 호출하여 현재 문자로 시작하는 부분 문자열이 프로그램이 찾고 있는 문자열과 일치하는지 여부를 확인합니다.

 

The StringBuilder Class

StringBuilder 객체는 수정할 수 있다는 점을 제외하면 String 객체와 같습니다. 내부적으로 이러한 객체는 일련의 문자를 포함하는 가변 길이 배열처럼 처리됩니다. 언제든지 메서드 호출을 통해 시퀀스의 길이와 내용을 변경할 수 있습니다.

스트링 빌더가 더 간단한 코드(이 섹션 끝에 있는 샘플 프로그램 참조)나 더 나은 성능 측면에서 이점을 제공하지 않는 한 항상 String을 사용해야 합니다. 예를 들어, 많은 수의 문자열을 연결해야 하는 경우 StringBuilder 객체에 추가하는 것이 더 효율적입니다.

 

Length and Capacity

String 클래스와 마찬가지로 StringBuilder 클래스에는 빌더의 문자 시퀀스 길이를 반환하는 length() 메서드가 있습니다.

문자열과 달리, 모든 StringBuilder는 문자 공간이 할당된 용량을 가지고 있습니다. capacity() 메서드에 의해 반환되는 이 용량은 항상 길이보다 크거나 같으며(보통 더 큽니다), 문자열 빌더에 추가가 필요할 때 자동으로 확장됩니다.

StringBuilder Constructors

Constructor Description
StringBuilder() 용량이 16개(16개 텅빈 엘리먼트)인 string builder를 생성합니다.
StringBuilder(CharSequence cs) 지정된 CharSequence와 동일한 문자와 CharSequence 뒤에 추가로 16개의 텅빈 엘리먼트를 포함하는 string builder를 생성합니다.
StringBuilder(int initCapacity) 지정된 초기 용량으로 빈 string builder 생성합니다.
StringBuilder(String s) 지정된 문자열과 문자열 뒤에 추가로 16개의 텅빈 엘리먼트를 사용하여 값이 초기화되는 string builder를 만듭니다.

 

예를 들면, 다음 코드는

// creates empty builder, capacity 16
StringBuilder sb = new StringBuilder();
// adds 9 character string at beginning
sb.append("Greetings");

 

길이가 9이고 용량이 16인 스트링 빌더를 생성합니다.

(이 sb 스트링빌더는 이제 7개의 문자를 더 저장할 수 있는 공간을 가집니다)

 

StringBuilder 클래스는 String 클래스에는 없는 길이와 용량에 관련된 몇 가지 메서드를 가지고 있습니다.

Length and Capacity Methods

Method Description
void setLength(int newLength) 문자 시퀀스의 길이를 설정합니다. newLength가 length()보다 작으면 문자 시퀀스의 마지막 문자가 잘립니다. newLength가 length()보다 크면 문자 시퀀스 끝에 널 문자가 추가됩니다.
void ensureCapacity(int minCapacity) 용량이 지정된 최소값 이상인지 확인합니다.

 

여러 작업(예: append(), insert() 또는 setLength())을 통해 스트링 빌더의 문자 시퀀스 길이를 늘려 결과 length()가 현재 capacity()보다 커질 수 있습니다. 이 경우 용량이 자동으로 증가됩니다.

 

StringBuilder Operations

String에서 사용할 수 없는 StringBuilder의 주요 작업은 모든 유형의 데이터를 허용하도록 오버로드되는 append() 및 insert() 메서드입니다. 각각은 해당 인수를 문자열로 변환한 다음 해당 문자열의 문자를 문자열 작성기의 문자 시퀀스에 추가하거나 삽입합니다. 추가 메소드는 항상 기존 문자 시퀀스의 끝에 이러한 문자를 추가하는 반면, 삽입 메소드는 지정된 지점에 문자를 추가합니다.

 

다음은 StringBuilder 클래스의 여러 메서드입니다.

 

Method Description
StringBuilder append(boolean b)
StringBuilder append(char c)
StringBuilder append(char[] str)
StringBuilder append(char[] str, int offset, int len)
StringBuilder append(double d)
StringBuilder append(float f)
StringBuilder append(int i)
StringBuilder append(long lng)
StringBuilder append(Object obj)
StringBuilder append(String s)
이 스트링 빌더에 아규먼트를 추가합니다. 추가 작업이 수행되기 전에 데이터가 문자열로 변환됩니다.
StringBuilder delete(int start, int end)
StringBuilder deleteCharAt(int index)
첫 번째 방법은 StringBuilder의 char 시퀀스에서 시작부터 end-1(포함)까지의 하위 시퀀스를 삭제합니다. 두 번째 방법은 index에 위치한 문자를 삭제하는 것이다.
StringBuilder insert(int offset, boolean b)
StringBuilder insert(int offset, char c)
StringBuilder insert(int offset, char[] str)
StringBuilder insert(int index, char[] str, int offset, int len)
StringBuilder insert(int offset, double d)
StringBuilder insert(int offset, float f)
StringBuilder insert(int offset, int i)
StringBuilder insert(int offset, long lng)
StringBuilder insert(int offset, Object obj)
StringBuilder insert(int offset, String s)
두 번째 아규먼트를 스트링 빌더에 삽입합니다. 첫 번째 정수 아규먼트는 데이터가 삽입되기 전의 인덱스를 나타냅니다. 삽입 작업이 수행되기 전에 데이터가 문자열로 변환됩니다.
StringBuilder replace(int start, int end, String s)
void setCharAt(int index, char c)
이 스트링 빌더에서 지정된 문자를 대체합니다.
StringBuilder reverse() 이 스트링 빌더의 문자 순서를 반대로 바꿉니다.
String toString() 빌더의 문자 시퀀스가 ​​포함된 문자열을 반환합니다.

참고: StringBuilder 객체에서 String 메서드를 사용하려면 먼저 StringBuilder 클래스의 toString() 메서드를 사용하여 String로 변환해야 합니다. 그런 다음, StringBuilder(String str) 생성자를 사용하여 String을 다시 StringBuilder로 변환할 수 있습니다.


 

An Example

"Strings" 섹션에 나열된 StringDemo 프로그램은 String 대신 StringBuilder를 사용하는 경우 더 효율적인 프로그램의 예입니다.StringDemo는 회문을 반전시켰습니다. 다시 한번 그 목록은 다음과 같습니다.

public class StringDemo {
    public static void main(String[] args) {
        String palindrome = "Dot saw I was Tod";
        int len = palindrome.length();
        char[] tempCharArray = new char[len];
        char[] charArray = new char[len];
        
        // put original string in an 
        // array of chars
        for (int i = 0; i < len; i++) {
            tempCharArray[i] = 
                palindrome.charAt(i);
        } 
        
        // reverse array of chars
        for (int j = 0; j < len; j++) {
            charArray[j] =
                tempCharArray[len - 1 - j];
        }
        
        String reversePalindrome =
            new String(charArray);
        System.out.println(reversePalindrome);
    }
}

 

이 프로그램을 실행시키면, 다음과 같은 출력이 만들어집니다.

doT saw I was toD

 

문자열 반전을 수행하기 위해 프로그램은 문자열을 문자 배열(첫 번째 for 루프)로 변환하고, 배열을 두 번째 배열(두 번째 for 루프)로 반전한 다음 다시 문자열로 변환합니다.

회문 문자열을 StringBuilder로 변환하는 경우 StringBuilder 클래스의 reverse() 메서드를 사용할 수 있습니다. 코드를 더 간단하고 읽기 쉽게 만듭니다.

public class StringBuilderDemo {
    public static void main(String[] args) {
        String palindrome = "Dot saw I was Tod";
         
        StringBuilder sb = new StringBuilder(palindrome);
        
        sb.reverse();  // reverse it
        
        System.out.println(sb);
    }
}

 

이 프로그램을 실행시키면, 다음과 같은 출력이 만들어집니다

doT saw I was toD

 

println()은 다음과 같이 스트링 빌더를 프린트합니다.

System.out.println(sb);

 

왜냐하면 sb.toString()은 println() 호출 시 다른 객체와 마찬가지로 암시적으로 호출되기 때문입니다.


참고: 메서드가 동기화되어 스레드로부터 안전하다는 점을 제외하면 StringBuilder 클래스와 정확히 동일한 StringBuffer 클래스도 있습니다. 스레드는 동시성에 대한 강의에서 논의됩니다.