본문으로 건너뛰기

5.2 String 배열과 문자열 처리 (String Arrays)

배열의 타입이 String인 경우, 즉 여러 개의 문자열을 담을 수 있는 배열을 말합니다. 자바의 String은 가장 많이 사용되는 특별한 클래스이며, 배열과 함께 자주 활용됩니다.

1. String 배열의 선언과 초기화

// 빈 String 배열 생성 (기본값: null)
String[] names = new String[3];
System.out.println(names[0]); // null

// 값으로 초기화
String[] seasons = {"봄", "여름", "가을", "겨울"};

// 인덱스로 하나씩 대입
String[] name = new String[3];
name[0] = "Kim";
name[1] = "Lee";
name[2] = "Park";

2. String 배열 순회와 비교

String[] fruits = {"사과", "바나나", "오렌지", "포도"};

// 순회
for (String fruit : fruits) {
System.out.println(fruit);
}

// 문자열 비교: == 가 아닌 .equals() 사용!
String target = "바나나";
for (int i = 0; i < fruits.length; i++) {
if (fruits[i].equals(target)) {
System.out.println(target + "를 인덱스 " + i + "에서 찾았습니다.");
}
}
== vs .equals()

==은 참조(주소)를 비교하고, .equals()는 문자열 내용을 비교합니다.

String a = new String("hello");
String b = new String("hello");

System.out.println(a == b); // false (서로 다른 객체)
System.out.println(a.equals(b)); // true (내용이 같음)

// 문자열 리터럴은 == 비교가 true로 나올 수 있지만, 항상 .equals()를 사용하세요.
String c = "hello";
String d = "hello";
System.out.println(c == d); // true (String pool에서 같은 객체)
System.out.println(c.equals(d)); // true

3. String이 불변(Immutable)인 이유와 장점

자바에서 String 객체는 한 번 생성되면 내용을 변경할 수 없습니다.

String s = "Hello";
s = s + " World"; // 새 String 객체 생성, s가 새 객체를 가리킴

// "Hello" 객체는 여전히 힙에 존재 (가비지 컬렉터가 정리)
// s는 이제 "Hello World" 라는 새 객체를 참조

불변성의 장점:

  1. 스레드 안전: 여러 스레드에서 동시에 같은 문자열을 읽어도 안전합니다.
  2. String Pool: 같은 리터럴은 하나의 객체를 공유하여 메모리 절약
  3. 해시 코드 캐싱: HashMap의 키로 안전하게 사용 가능
  4. 보안: 비밀번호, URL 등이 변경되지 않음을 보장
// 문자열 수정이 잦다면 StringBuilder를 사용
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
sb.append(i).append(", ");
}
sb.delete(sb.length() - 2, sb.length()); // 마지막 ", " 제거
System.out.println(sb.toString()); // 1, 2, 3, 4, 5

4. String 내부 구조: char[] 배열

String은 내부적으로 char[] 배열로 이루어져 있습니다.

String str = "Hello";

// charAt()으로 특정 위치의 문자 접근
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i) + " "); // H e l l o
}

// toCharArray()로 char[] 배열로 변환
char[] chars = str.toCharArray();
System.out.println(chars[0]); // H

// char[] 배열을 다시 String으로 변환
String restored = new String(chars);
System.out.println(restored); // Hello

5. String 주요 메서드 완전 정리

길이 및 문자 접근

String s = "Hello, Java!";

// 길이
System.out.println(s.length()); // 12

// 특정 위치 문자
System.out.println(s.charAt(0)); // H
System.out.println(s.charAt(7)); // J

// 부분 문자열
System.out.println(s.substring(7)); // Java!
System.out.println(s.substring(7, 11)); // Java (7 이상 11 미만)

검색

String s = "Hello, Java! Hello!";

// 포함 여부
System.out.println(s.contains("Java")); // true

// 위치 찾기 (없으면 -1)
System.out.println(s.indexOf("Hello")); // 0 (첫 번째)
System.out.println(s.lastIndexOf("Hello")); // 13 (마지막)
System.out.println(s.indexOf("Python")); // -1

// 시작/끝 확인
System.out.println(s.startsWith("Hello")); // true
System.out.println(s.endsWith("!")); // true

변환 및 치환

String s = "  Hello, Java!  ";

// 공백 제거
System.out.println(s.trim()); // "Hello, Java!" (앞뒤 공백 제거, 레거시)
System.out.println(s.strip()); // "Hello, Java!" (유니코드 공백 포함, Java 11+)

// 대소문자 변환
System.out.println(s.trim().toUpperCase()); // HELLO, JAVA!
System.out.println(s.trim().toLowerCase()); // hello, java!

// 치환
String original = "I love Java. Java is great.";
System.out.println(original.replace("Java", "Python"));
// I love Python. Python is great.

분리와 결합

// 분리
String csv = "사과,바나나,오렌지,포도";
String[] fruits = csv.split(",");
System.out.println(fruits.length); // 4
for (String f : fruits) {
System.out.print(f + " "); // 사과 바나나 오렌지 포도
}

// 결합
String joined = String.join(" - ", fruits);
System.out.println(joined); // 사과 - 바나나 - 오렌지 - 포도

// 또는 String.join()
System.out.println(String.join(", ", "A", "B", "C")); // A, B, C

포맷

// String.format()
String name = "홍길동";
int age = 25;
double gpa = 3.85;

String info = String.format("이름: %s, 나이: %d, GPA: %.2f", name, age, gpa);
System.out.println(info); // 이름: 홍길동, 나이: 25, GPA: 3.85

// Java 15+: formatted()
String info2 = "이름: %s, 나이: %d".formatted(name, age);
System.out.println(info2); // 이름: 홍길동, 나이: 25

비교

String a = "Apple";
String b = "apple";

System.out.println(a.equals(b)); // false (대소문자 구분)
System.out.println(a.equalsIgnoreCase(b)); // true (대소문자 무시)
System.out.println(a.compareTo(b)); // 음수 (A < a, 사전순)
System.out.println(a.compareToIgnoreCase(b)); // 0 (같음)

기타 유용한 메서드 (Java 11+)

String empty = "   ";
String blank = "";

System.out.println(empty.isBlank()); // true (공백만 있어도 true)
System.out.println(blank.isEmpty()); // true (길이가 0)
System.out.println("hello".repeat(3)); // hellohellohello

// lines() - 줄 단위로 분리 (Stream 반환)
"line1\nline2\nline3".lines().forEach(System.out::println);

6. 문자열 배열 정렬

import java.util.Arrays;
import java.util.Comparator;

String[] names = {"Charlie", "Alice", "Bob", "David", "Eve"};

// 사전순(오름차순) 정렬
Arrays.sort(names);
System.out.println(Arrays.toString(names));
// [Alice, Bob, Charlie, David, Eve]

// 사전 역순(내림차순) 정렬
Arrays.sort(names, Comparator.reverseOrder());
System.out.println(Arrays.toString(names));
// [Eve, David, Charlie, Bob, Alice]

// 길이 순 정렬
Arrays.sort(names, Comparator.comparingInt(String::length));
System.out.println(Arrays.toString(names));
// [Bob, Eve, Alice, David, Charlie]

// 길이 순, 같은 길이면 사전순
Arrays.sort(names, Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()));
System.out.println(Arrays.toString(names));

7. char 배열과 String 변환

// String → char[]
String str = "Hello";
char[] chars = str.toCharArray();

// char[] 출력
System.out.println(chars); // Hello (char[] 직접 출력 가능)
System.out.println(new String(chars)); // Hello

// char[] 조작 후 String으로
chars[0] = 'J';
String modified = new String(chars);
System.out.println(modified); // Jello

// 문자 하나씩 확인
for (char c : chars) {
System.out.print(c + "(" + (int) c + ") "); // J(74) e(101) l(108) l(108) o(111)
}

8. 실전 예제: 문장을 단어 배열로 분리하여 처리

import java.util.Arrays;

public class WordProcessor {
public static void main(String[] args) {
String sentence = " Java is a powerful and versatile programming language ";

// 1. 앞뒤 공백 제거 후 단어 분리 (정규식 \\s+: 여러 공백도 처리)
String[] words = sentence.trim().split("\\s+");

System.out.println("단어 수: " + words.length);
System.out.println("단어 목록: " + Arrays.toString(words));

// 2. 각 단어의 길이 계산
System.out.println("\n단어별 길이:");
for (String word : words) {
System.out.printf("%-15s: %d글자%n", word, word.length());
}

// 3. 가장 긴 단어 찾기
String longest = words[0];
for (String word : words) {
if (word.length() > longest.length()) {
longest = word;
}
}
System.out.println("\n가장 긴 단어: " + longest + " (" + longest.length() + "글자)");

// 4. 특정 글자로 시작하는 단어 필터링
char initial = 'p';
System.out.println("\n'" + initial + "'로 시작하는 단어:");
for (String word : words) {
if (word.toLowerCase().startsWith(String.valueOf(initial))) {
System.out.println(" " + word);
}
}

// 5. 모든 단어를 대문자로 변환하여 재결합
String[] upperWords = new String[words.length];
for (int i = 0; i < words.length; i++) {
upperWords[i] = words[i].toUpperCase();
}
System.out.println("\n대문자 변환: " + String.join(" ", upperWords));

// 6. 알파벳 순 정렬
Arrays.sort(words);
System.out.println("\n정렬된 단어: " + Arrays.toString(words));
}
}

출력:

단어 수: 8
단어 목록: [Java, is, a, powerful, and, versatile, programming, language]

단어별 길이:
Java : 4글자
is : 2글자
a : 1글자
powerful : 8글자
and : 3글자
versatile : 9글자
programming : 11글자
language : 8글자

가장 긴 단어: programming (11글자)

'p'로 시작하는 단어:
powerful
programming

대문자 변환: JAVA IS A POWERFUL AND VERSATILE PROGRAMMING LANGUAGE

정렬된 단어: [Java, a, and, is, language, powerful, programming, versatile]
고수 팁

문자열 처리 핵심 패턴:

  1. null 안전 비교: "상수".equals(변수) 패턴으로 NullPointerException 방지
String input = null;
// input.equals("hello") → NullPointerException!
"hello".equals(input) // → false, 안전
  1. StringBuilder로 반복 문자열 조합:
// 나쁜 예: 반복마다 새 String 객체 생성
String result = "";
for (int i = 0; i < 1000; i++) result += i + ", ";

// 좋은 예: StringBuilder 사용
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append(i).append(", ");
String result = sb.toString();
  1. 빈 문자열 체크: str.isEmpty()(길이 0), str.isBlank()(공백만)로 구분