[Coding Test] 유용한 JAVA 문법
Collections
Collection
vsCollections
Collection
: 인터페이스.List
,Set
,Map
의 부모Collections
: 클래스. 컬렉션을 다루는 도구
Collections 란? (java.util.Collections
)
List
,Set
,Map
과 같은 컬렉션에 저장된 데이터를 다룰 때 사용하는 유틸리티 클래스.Collection
클래스를 이용하면 정렬하기, 최댓값 찾기, 특정 값으로 채우기 등 다양한 작업을 쉽게 수행할 수 있다.
1. 정렬과 순서 제어 (Sorting & Ordering)
sort()
- 리스트 정렬
Collections.sort(List<T> list)
- 리스트를 오름차순으로 정렬한다.
- 리스트의 요소가
Comparable
인터페이스를 구현하고 있어야 한다. - (
Integer
,String
등은 이미 구현되어 있음)
Collections.sort(List<T> list, Comparator<? super T> c)
- 직접 만든
Comparator
의 규칙에 따라 리스트를 정렬한다. - 내림차순이나 복잡한 기준으로 정렬할 때 사용한다.
reverseOrder()
: 내림차순 ‘규칙’ 제공
reverse()와 이름이 비슷하지만, reverseOrder()는 행동이 아니라 ‘내림차순’이라는 정렬 규칙(Comparator)을 만들어 반환한다.
Java 8부터는 List.sort()와 Comparator.reverseOrder()를 사용하는 것이 더 권장된다.- 직접 만든
1
2
3
4
5
6
7
8
9
List<Integer> numbers = new ArrayList<>(Arrays.asList(4, 1, 5, 2, 3));
// 오름차순 정렬
Collections.sort(numbers);
// numbers -> [1, 2, 3, 4, 5]
// 내림차순 정렬 (Comparator 사용)
Collections.sort(numbers, Collections.reverseOrder());
// numbers -> [5, 4, 3, 2, 1]
reverse()
- 순서 뒤집기
1
2
3
List<Integer> list = new ArrayList<>(Arrays.asList(1, 4, 2, 5));
Collections.reverse(list);
// list -> [5, 2, 4, 1] (정렬이 아니라 순서만 뒤집힘)
shuffle()
- 무작위로 섞기
1
2
Collections.shuffle(list);
// list -> [4, 1, 5, 2] (실행할 때마다 결과가 바뀜)
2. 검색과 탐색 (Searching & Finding)
binarySearch()
- 이진 탐색
- 반드시 정렬된 리스트에서 특정 값의 인덱스를 찾는다.
- 정렬되지 않은 리스트에서 사용하면 잘못된 결과를 반환한다.
1
2
List<Integer> sortedList = Arrays.asList(10, 20, 30, 40, 50);
int index = Collections.binarySearch(sortedList, 30); // index -> 2
max()
/ min()
- 최댓값 / 최솟값 찾기
- 컬렉션 전체에서 가장 크거나 작은 요소를 찾아 반환한다.
1
2
int maxValue = Collections.max(sortedList); // 50
int minValue = Collections.min(sortedList); // 10
frequency()
- 빈도수 계산
- 컬렉션에서 특정 요소가 몇 번 등장하는지 계산한다.
1
2
List<String> fruits = Arrays.asList("apple", "banana", "apple", "orange");
int count = Collections.frequency(fruits, "apple"); // count -> 2
3. 데이터 조작
replaceAll()
- 값 교체
- 리스트 내의 특정 값을 찾아 다른 값으로 모두 교체한다.
1
2
Collections.replaceAll(fruits, "apple", "grape");
// fruits -> ["grape", "banana", "grape", "orange"]
람다(Lambda)와 스트림(Stream) API
람다와 스트림이란?
람다(Lambda) : 이름 없는 함수(Anonymous Function). 메서드의 인자로 ‘코드(동작)’를 전달할 수 있게 해주는 문법.
스트림(Stream) : 데이터의 흐름. 컬렉션 데이터를 파이프라인처럼 여러 단계로 가공하고 처리하는 기능.
‘어떻게(HOW)’가 아닌 ‘무엇을(WHAT)’에 집중하는 선언적 프로그래밍을 통해 코드를 매우 간결하고 가독성 높게 만들어준다.
1. 람다 표현식 (Lambda Expression)
기존의 익명 내부 클래스 방식을 획기적으로 줄여주는 문법.
하나의 추상 메서드만 가진 인터페이스(함수형 인터페이스)를 구현할 때 사용.
Comparator
구현 예시
1
2
3
4
5
6
7
8
9
10
11
12
// Before (익명 클래스)
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
// After (람다 표현식)
list.sort((s1, s2) -> s1.length() - s2.length());
2. 스트림 API (Stream API)
스트림은 생성 → 중간 연산 → 최종 연산 3단계의 파이프라인으로 동작한다.
중간 연산은 여러 개를 연결할 수 있으며, 최종 연산이 호출될 때 한 번에 처리된다 (지연 연산).
코딩 테스트 필수 중간 연산
filter(조건)
: 조건에 맞는 데이터만 남긴다.map(함수)
: 각 데이터를 특정 형태로 변환한다.sorted()
: 데이터를 오름차순으로 정렬한다. (sorted(Comparator)로 기준 지정 가능)distinct()
: 중복된 데이터를 제거한다.
코딩 테스트 필수 최종 연산
collect(Collectors.toList())
: 스트림의 요소들을 모아 List로 만든다.forEach(동작)
: 각 요소에 대해 특정 동작을 수행한다. (주로 출력 시 사용)count()
: 요소의 개수를 센다.reduce(초기값, 연산)
: 모든 요소를 하나로 합치는 연산을 수행한다. (예: 합계 구하기)anyMatch() / allMatch() / noneMatch()
: 특정 조건을 만족하는 요소가 있는지(하나라도/모두/전혀 없는지) 확인한다.
3. 예제
예제 1: 백준 11286번 (절댓값 힙)
1
2
3
4
5
6
7
8
9
10
11
12
// 문제: 배열에서 절댓값이 가장 작은 값을 출력하기. 절댓값이 가장 작은 값이 여러개일 때는, 가장 작은 수를 출력하기.
PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> {
int first_abs = Math.abs(a);
int second_abs = Math.abs(b)
if(first_abs == second_abs){ // 절댓값이 같은 경우에는 내림차순
return a - b;
}
return first_abs - second_abs; // 절댓값이 다른 경우에는 작은 값 리턴
});
예제 2: 숫자 배열 가공
1
2
3
4
5
6
7
8
9
10
11
12
// 문제: 정수 배열에서 0보다 큰 수만 골라, 각 숫자를 제곱한 뒤, 내림차순으로 정렬하여 새로운 리스트 만들기
int[] arr = {1, -2, 3, -5, 4, 0};
List<Integer> result = Arrays.stream(arr) // 1. 배열로 스트림 생성
.filter(n -> n > 0) // 2. 양수만 필터링 [1, 3, 4]
.map(n -> n * n) // 3. 각자 제곱 [1, 9, 16]
.sorted(Comparator.reverseOrder()) // 4. 내림차순 정렬 [16, 9, 1]
.boxed() // 5. IntStream을 Stream<Integer>로 변환
.collect(Collectors.toList()); // 6. List로 수집
// result -> [16, 9, 1]
예제 3: 문자열 리스트 처리
1
2
3
4
5
6
7
8
9
10
// 문제: 파일 이름 리스트에서 "java" 확장자만 골라, 파일명 길이순으로 정렬하기
List<String> fileNames = Arrays.asList("solution.java", "test.cpp", "README.md", "main.java");
List<String> javaFiles = fileNames.stream()
.filter(name -> name.endsWith(".java"))
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
// javaFiles -> ["main.java", "solution.java"]
정규 표현식 (Regular Expressions)
정규 표현식이란?
문자열에서 특정 패턴을 검색, 대체, 추출하기 위해 사용되는 ‘패턴 매칭 언어’.
복잡한 규칙을 가진 문자열을 다룰 때, 코드를 매우 간결하게 만들어주는 강력한 도구.
코딩 테스트에서는 주로 문자열 파싱, 유효성 검사, 특정 패턴 추출 등에 활용된다.
자주 사용하는 정규 표현식 문법
종류 | 문법 | 설명 |
---|---|---|
문자 | . | 임의의 한 문자 (줄바꿈 문자 제외) |
\d | 숫자 (0-9) | |
\D | 숫자가 아닌 문자 | |
\w | 알파벳, 숫자, 언더스코어(_) 중 하나 | |
\W | \w 가 아닌 문자 (특수 문자 등) | |
\s | 공백 문자 (스페이스, 탭, 줄바꿈 등) | |
\S | 공백 문자가 아닌 문자 | |
경계 | ^ | 문자열의 시작 |
$ | 문자열의 끝 | |
\b | 단어의 경계 | |
그룹 | [...] | 대괄호 안의 문자 중 하나 (예: [abc] 는 a, b, c 중 하나) |
[^...] | 대괄호 안의 문자를 제외한 나머지 문자 (예: [^0-9] 는 숫자가 아님) | |
(...) | 그룹화. 패턴을 하나의 단위로 묶음 | |
A\|B | A 또는 B | |
수량자 | * | 0번 이상 반복 |
+ | 1번 이상 반복 | |
? | 0번 또는 1번 등장 | |
{n} | 정확히 n번 반복 | |
{n,} | n번 이상 반복 | |
{n,m} | n번 이상, m번 이하 반복 |
Java에서 정규 표현식 사용하기
1. String.matches()
- 문자열 전체가 주어진 정규 표현식과 완전히 일치하는지 검사한다.
boolean
값을 반환하여 유효성 검사에 주로 사용된다.
1
2
3
4
String email = "test@example.com";
String emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$";
boolean isValid = email.matches(emailRegex); // true
2. String.replaceAll()
/ String.split()
replaceAll(regex, replacement)
: 문자열에서 정규 표현식에 맞는 모든 부분을 찾아 다른 문자열로 교체한다.split(regex)
: 정규 표현식을 기준으로 문자열을 분리하여 문자열 배열로 반환한다.
1
2
3
4
5
6
7
8
9
10
String text = "전화번호는 010-1234-5678 입니다. 집은 02-987-6543 입니다.";
// 숫자만 추출하기 (숫자가 아닌 모든 문자를 ""으로 교체)
String numbers = text.replaceAll("[^0-9]", "");
// numbers -> "01012345678029876543"
// 공백, 쉼표, 하이픈을 기준으로 문자열 분리
String data = "사과,바나나-딸기 포도";
String[] fruits = data.split("[,\\s-]"); // 쉼표, 공백, 하이픈 중 하나를 기준으로 분리
// fruits -> ["사과", "바나나", "딸기", "포도"]
3. Pattern
과 Matcher
클래스
- 더 복잡하고 세밀한 정규 표현식 처리가 필요할 때 사용되는 정석적인 방법.
Pattern
: 정규 표현식 패턴을 컴파일하여 객체로 만듦.Matcher
:Pattern
객체를 이용해 실제 문자열과 매칭 작업을 수행.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String source = "ID: user1, ID: user2, ID: user3";
String regex = "ID: (\\w+)"; // ID: 뒤의 단어(알파벳/숫자)를 그룹으로 캡처
Pattern pattern = Pattern.compile(regex); // 1. 패턴 컴파일
Matcher matcher = pattern.matcher(source); // 2. 매처 생성
// matcher.find() : 패턴과 일치하는 다음 부분을 찾음
while (matcher.find()) {
// matcher.group(0) : 매칭된 전체 문자열 (e.g., "ID: user1")
// matcher.group(1) : 첫 번째 그룹에 매칭된 문자열 (e.g., "user1")
System.out.println(matcher.group(1));
}
// 출력:
// user1
// user2
// user3