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" 라는 새 객체를 참조
불변성의 장점:
- 스레드 안전: 여러 스레드에서 동시에 같은 문자열을 읽어도 안전합니다.
- String Pool: 같은 리터럴은 하나의 객체를 공유하여 메모리 절약
- 해시 코드 캐싱:
HashMap의 키로 안전하게 사용 가능 - 보안: 비밀번호, 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]
고수 팁
문자열 처리 핵심 패턴:
- null 안전 비교:
"상수".equals(변수)패턴으로 NullPointerException 방지
String input = null;
// input.equals("hello") → NullPointerException!
"hello".equals(input) // → false, 안전
- 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();
- 빈 문자열 체크:
str.isEmpty()(길이 0),str.isBlank()(공백만)로 구분