알고리즘 문제를 풀면서 하나의 문자열이 다른 문자열 안에 포함되어 있는지 확인하는 문제들을 종종 볼 수 있다.
https://school.programmers.co.kr/learn/courses/30/lessons/120908
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
물론 위의 문제는 굉장히 쉬운 문제이다.
필자 같은 경우는 과거에는 문자열 길이가 너무 길지 않다면 그냥 하나의 문자열을 기준을 잡고 한 칸씩
오른쪽으로 밀면서 비교를 해서 풀었던 거 같다.
물론 브루스 포스 방식이 꼭 나쁘진 않고 이 안에서도 첫 번째 글자가 우리가 찾는 글자가 맞는지 확인을 하는 조건을 추가한다면
나름 나쁘지 않게 구현을 할 수 는 있다.
알고리즘을 공부하는 입장에서는 직접 구현도 매우 좋지만 만약 내가 개발할 때 직접 코드를 치는 상황에서는 이보다 로직 자체도
복잡할 뿐더러 내가 직접 구현한 함수이기 때문에 오류가 없는지 검증하고 최적화를 시켜야 한다.
이러한 문제를 해결해 줄 자바 메서드를 하나 소개하겠다.
String.contains()
1.String.contains() 사용법
먼저 어떻게 쓰는지 간단한 예제 코드를 봐보자
public class Main {
public static void main(String[] args) {
String fullText = "ab6CDE443fgh22iJKlmn1o";
String target = "6CD";
// target이 fullText에 포함되어 있는지 확인 (boolean 반환)
if (fullText.contains(target)) {
System.out.println("포함되어 있습니다.");
} else {
System.out.println("포함되어 있지 않습니다.");
}
}
}
if (fullText.contains(target)) 이 한줄로 모든 비교가 끝났다.
만약 인덱스 위치까지 알고 싶다면 이렇게 하면 된다.
int index = fullText.indexOf(target);
if (index != -1) {
System.out.println("인덱스 " + index + "번 위치에 존재합니다.");
} else {
System.out.println("찾을 수 없습니다.");
}
그리고 대소문자를 구분하고 싶지 않다면 다음 방식을 추가하면 된다.
boolean found = fullText.toLowerCase().contains(target.toLowerCase());
그리고 위에 프로그래머스 문제도 필자는 다음처럼 풀었다.
class Solution {
public int solution(String str1, String str2) {
int answer = 2;
if(str1.contains(str2)) {
answer = 1;
}
return answer;
}
}
물론 알고리즘을 공부하는 관점에서는 개인적으로 메서드 호출은 지양하는 것이 좋다고 생각한다.
내가 직접 손으로 구현해야지 메서드에 의존을 하다보면 실력이 절대 늘지 않는다.
2. String.contains() 동작 방식
이제 어떻게 쓸지는 대충 알았다.
근데 여기서 끝내면 안된다.
내부적으로 어떻게 동작하는지도 한 번 확인해보자
먼저 소스코드 내부를 봐보자
public boolean contains(CharSequence s) {
return indexOf(s.toString()) >= 0;
}
매우 단순하다.
결국 핵심은 indexOf()가 문자열을 어떻게 찾느냐에 달려 있다.
3. indexOf()의 탐색 알고리즘 알아보기
자바 8~11 버전 기준으로 indexOf()는 기본적으로 나이브(Naive) 탐색 방식을 최적화하여 사용한다.
핵심 동작 과정:
- 첫 번째 문자 탐색: 타겟 문자열(6CD)의 첫 글자인 '6'이 나타날 때까지 전체 문자열을 빠르게 훑습니다. (이 과정은 CPU 레벨에서 매우 빠르게 처리된다.)
- 검증: '6'을 발견하면, 그 뒤의 문자들('C', 'D')이 일치하는지 확인한다.
- 불일치 시 복귀: 만약 중간에 'D'가 아니라 다른 글자가 나온다면, 다시 첫 번째 문자 탐색 모드로 돌아간다.
substring().equals() 방식 vs 자바 내부 방식의 차이
substring().equals() 방식을 쓰는 것과 자바 내부 방식의 가장 큰 차이는 메모리 사용량이다.
- substring().equals(): 매 칸마다 새로운 문자열 객체를 메모리에 계속 생성한다. (버려지는 쓰레기 값이 많아짐)
- 자바 내부 방식: 객체를 새로 만들지 않고, 원본 문자열의 char[] 배열에 인덱스로 직접 접근하여 비교한다. 메모리 낭비가 전혀 없다.
4. 최신 자바(Java 12+)에서의 indexOf() 진화
최신 버전의 자바(HotSpot JVM)는 이 과정을 더 똑똑하게 처리한다.
- SIMD(Single Instruction Multiple Data) 사용: 최신 CPU의 특수 명령어를 사용하여, 한 번에 문자 한 개가 아니라 여러 개의 문자를 동시에 비교한다. (벡터화 최적화)
- Intrinsic(내재 함수): indexOf 같은 빈번한 작업은 자바 코드가 아닌, CPU가 바로 이해할 수 있는 어셈블리어 수준의 최적화 코드로 대체되어 실행된다.
결론
만약 내가 알고리즘 공부가 목적이라면 내가 손으로 직접 구현을 하는 방식을 채택할 것이다.
그러나 개발 단계에서 문자열 비교를 하는 경우 위와 같이 자바 내장 함수를 사용할 거 같다.
혹여나 내가 내장 함수 정도로 최적화 하여 구현할 수 있다 할지라도 코드 가독성 면에서 함수를 쓰는 것이 좀 더 효울적이므로
내가 구현하는 것은 지양할 거 같다.
'tech > JAVA' 카테고리의 다른 글
| [JAVA] 문자열 비교 == 연산자와 .equals()의 차이 (1) | 2025.12.14 |
|---|