공공부하자개발 · 영어 학습 노트
자바
명령어자주 쓰는 API 참조0/8 완료
  • 01String · StringBuilder · 정규식
  • 02Collections · Arrays · Map
  • 03Stream · Collectors · 함수형 인터페이스
  • 04Files · Path · I/O
  • 05java.time · BigDecimal · 기타 유틸
  • 06Oracle SQL
  • 07예외 · 디버깅
  • 08Spring Boot 설정 키
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 명령어 › 02 / 8

Collections · Arrays · Map

섹션 16진행 0 / 8

Collections · Arrays · Map 명령어

List/Set/Map/Queue/Deque의 주요 메서드, 생성 방법, 정렬·검색·변환, Collections/Arrays 유틸을 표로 정리했다. 원리는 중급 05 레슨에서 다루고, 여기서는 어떤 메서드가 있고 결과가 무엇인지만 빠르게 찾는다. Ctrl+F로 메서드명(예: computeIfAbsent, subList, binarySearch)을 검색하면 된다. 모든 결과는 JDK 21 실행값이다.

01한눈에 보기
메서드 설명 예제 결과
List.of(...) 불변 리스트 List.of(1, 2, 3) [1, 2, 3]
new ArrayList<>(coll) 가변 복사본 new ArrayList<>(List.of(1, 2)) [1, 2]
list.add(x) / add(i, x) 끝/위치에 추가 l.add(2); l.add(0, 0) (l=[1]) [0, 1, 2]
list.get(i) 조회 List.of(3, 1, 4).get(0) 3
list.remove(int) vs remove(Object) 인덱스 vs 값 삭제 l.remove(Integer.valueOf(3)) (l=[1,2,3]) [1, 2]
list.contains(x) 포함 여부 O(n) List.of(3, 1, 4).contains(4) true
list.sort(cmp) 정렬(null=자연순) l.sort(Comparator.reverseOrder()) (l=[3,1,2]) [3, 2, 1]
list.getFirst() / getLast() 첫/끝 (JDK 21) List.of(3, 1, 4).getLast() 4
list.reversed() 역순 뷰 (JDK 21) List.of(3, 1, 4).reversed() [4, 1, 3]
new HashSet<>(coll) 중복 제거 new HashSet<>(List.of(1, 1, 2)) [1, 2]
set.add(x) 추가 성공 여부 s.add(1) (이미 있음) false
map.get(k) / getOrDefault(k, d) 조회 m.getOrDefault("zzz", 0) 0
map.put(k, v) 삽입/교체, 이전 값 반환 m.put("a", 10) (기존 1) 1
map.computeIfAbsent(k, f) 없으면 만들어 넣고 반환 g.computeIfAbsent("x", k -> new ArrayList<>()).add(1) {x=[1]}
map.merge(k, v, f) 카운팅/누적 c.merge("a", 1, Integer::sum) ×2 {a=2}
map.entrySet() 순회 for (var e : m.entrySet()) e.getKey() 키 순회
Collections.sort(list) 정렬 Collections.sort(l) (l=[3,1,2]) [1, 2, 3]
Arrays.asList(arr) 고정 크기 리스트 뷰 Arrays.asList(1, 2) [1, 2]
Arrays.sort(arr) 배열 정렬 Arrays.sort(a) (a={5,3,1}) [1, 3, 5]
Arrays.toString(arr) 배열 출력 Arrays.toString(new int[]{1, 2}) [1, 2]
02생성: 불변 vs 가변
메서드/시그니처 설명 한 줄 예제 결과
List.of(e...) 불변, null 불가 List.of(1, 2, 3) [1, 2, 3]
List.of(...).add(x) 수정 시 예외 List.of(1).add(2) UnsupportedOperationException
List.of(1, null) null 불가 List.of(1, null) NullPointerException
List.copyOf(coll) 불변 스냅샷 var v = List.copyOf(src); src.add(2); v [1] (원본 변경 무관)
Set.of(e...) / Map.of(k, v, ...) 불변 (중복 키 불가) Map.of("a", 1, "a", 2) IllegalArgumentException: duplicate key: a
Map.ofEntries(Map.entry(k, v), ...) 11개 이상 쌍 Map.ofEntries(Map.entry("k", 1), Map.entry("j", 2)).size() 2
Map.copyOf(map) 불변 복사 Map.copyOf(new HashMap<>(Map.of("a", 1))) {a=1}
new ArrayList<>(coll) 가변 복사본 new ArrayList<>(List.of(1, 2)) [1, 2]
new ArrayList<>(n) 초기 용량 new ArrayList<>(1000) [] (size 0)
Arrays.asList(arr) 고정 크기 뷰 (set 가능, add 불가) Arrays.asList(1, 2).add(3) UnsupportedOperationException
Arrays.asList set 원본 배열과 연동 Integer[] a = {1, 2}; var l = Arrays.asList(a); a[0] = 9; l [9, 2]
Collections.unmodifiableList(l) 불변 뷰 (원본 바뀌면 같이 바뀜) var v = unmodifiableList(src); src.add(2); v [1, 2]
Collections.emptyList() 빈 불변 Collections.emptyList() []
Collections.singletonList(x) 원소 1개 불변 Collections.singletonList(1) [1]
Collections.nCopies(n, x) 반복 불변 Collections.nCopies(3, "x") [x, x, x]
Stream.toList() 불변 (JDK 16+) Stream.of(1).toList().add(2) UnsupportedOperationException
Collectors.toList() 가변 (ArrayList, 명세상 보장은 없음) Stream.of(1).collect(Collectors.toList()).add(2) [1, 2]
new LinkedHashSet<>(coll) 삽입 순서 유지 Set new LinkedHashSet<>(List.of("banana", "apple", "cherry")) [banana, apple, cherry]
new TreeSet<>(coll) 정렬 Set new TreeSet<>(List.of("banana", "apple", "cherry")) [apple, banana, cherry]
new TreeMap<>(cmp) 정렬 Map + 비교자 new TreeMap<String,Integer>(Comparator.reverseOrder()) + put a,b {b=2, a=1}
new EnumMap<>(Enum.class) enum 키 전용, 선언 순서 new EnumMap<>(Map.of(State.RUNNABLE, 1, State.NEW, 0)) {NEW=0, RUNNABLE=1}
EnumSet.of(...) / allOf(cls) enum 전용 Set EnumSet.allOf(DayOfWeek.class).size() 7
  • List.of는 contains(null)도 NPE를 던진다. null이 섞일 수 있으면 new ArrayList<>().
  • 메서드 반환 타입은 인터페이스(List<T>)로, 구현체는 생성 시점에만 명시한다.
03List
메서드/시그니처 설명 한 줄 예제 (l = [1, 2, 3] 기준) 결과
add(E) / add(int, E) 추가 l.add(4); l.add(0, 0) [0, 1, 2, 3, 4]
addAll(Collection) 전부 추가 l.addAll(List.of(4, 5)) [1, 2, 3, 4, 5]
get(int) 조회 (범위 밖 예외) List.of(1).get(1) IndexOutOfBoundsException: Index: 1 Size: 1
set(int, E) 교체, 이전 값 반환 l.set(0, 9) [9, 2, 3]
remove(int) 인덱스 삭제 l.remove(0) [2, 3]
remove(Object) 값 삭제 (첫 개, boolean) l.remove(Integer.valueOf(3)) [1, 2]
removeIf(Predicate) 조건 삭제 (안전) l.removeIf(x -> x % 2 == 0) [1, 3]
removeAll(Collection) 차집합 l.removeAll(List.of(2, 3)) [1]
retainAll(Collection) 교집합 l.retainAll(List.of(2, 3)) [2, 3]
replaceAll(UnaryOperator) 전체 변환 l.replaceAll(x -> x * 10) [10, 20, 30]
clear() 비우기 l.clear() []
size() / isEmpty() 크기 l.size() 3
contains(Object) 포함 O(n) l.contains(4) false
containsAll(Collection) 모두 포함 l.containsAll(List.of(1, 3)) true
indexOf(Object) / lastIndexOf 위치 (-1) List.of(3, 1, 4, 1).lastIndexOf(1) 3
subList(from, to) 뷰 [from, to) List.of(3, 1, 4, 1, 5).subList(1, 3) [1, 4]
subList().clear() 구간 삭제 관용구 l.subList(1, 3).clear() (l=[1,2,3,4]) [1, 4]
getFirst() / getLast() 첫/끝 (JDK 21, 비면 NoSuchElement) l.getFirst() 1
addFirst(E) / addLast(E) 앞/뒤 추가 (JDK 21) l.addFirst(0) [0, 1, 2, 3]
removeFirst() / removeLast() 앞/뒤 삭제 (JDK 21) l.removeFirst(); l.removeLast() [2]
reversed() 역순 뷰 (JDK 21) l.reversed() [3, 2, 1]
sort(Comparator) 제자리 정렬 (null=자연순) l.sort(null) (l=[3,1,2]) [1, 2, 3]
toArray(T[]::new) 배열로 l.toArray(Integer[]::new) Integer[]{1, 2, 3}
toArray(new T[0]) 배열로(고전) l.toArray(new Integer[0]) Integer[]{1, 2, 3}
stream() 스트림 l.stream().map(x -> x * 2).toList() [2, 4, 6]
forEach(Consumer) 순회 l.forEach(System.out::println) 1 2 3
iterator() 반복자 var it = l.iterator(); it.next() 1
listIterator() 양방향 + set/add it.next(); it.set(9); it.add(8) (l=[1,2]) [9, 8, 2]
listIterator(size) + hasPrevious 역순 순회 while (it.hasPrevious()) it.previous() 3, 2, 1
equals(Object) 순서 포함 내용 비교 List.of(1, 2).equals(List.of(2, 1)) false
hashCode() 내용 기반 List.of(1, 2).hashCode() 994
  • ArrayList: 인덱스 접근 O(1), 중간 삽입/삭제 O(n). LinkedList: 양끝 O(1), 인덱스 접근 O(n). 실무는 거의 ArrayList.
  • remove(int)와 remove(Object): List<Integer>에서 l.remove(1)은 인덱스 1 삭제다. 값 삭제는 Integer.valueOf(1).
  • subList는 뷰다. 원본이 구조적으로 바뀌면 ConcurrentModificationException.
04Set
메서드/시그니처 설명 한 줄 예제 결과
add(E) 추가, 이미 있으면 false s.add(1) (s={1}) / s.add(2) false / true
remove(Object) 삭제 성공 여부 s.remove(1); s.remove(9) (s={1,2}) true / false, [2]
contains(Object) HashSet O(1), TreeSet O(log n) Set.of(1, 2).contains(2) true
size() / isEmpty() / clear() 기본 Set.of().isEmpty() true
addAll(c) 합집합 a.addAll(Set.of(3, 4)) (a={1,2,3}) [1, 2, 3, 4]
retainAll(c) 교집합 a.retainAll(Set.of(3, 4)) [3]
removeAll(c) 차집합 a.removeAll(Set.of(3, 4)) [1, 2]
Collections.disjoint(a, b) 공통 원소 없음 Collections.disjoint(List.of(1, 2), List.of(3)) true
equals 순서 무관 Set.of(1, 2).equals(new HashSet<>(List.of(2, 1))) true
HashSet 순서 해시 순서 (보장 없음) new HashSet<>(List.of("banana", "apple", "cherry")) [banana, apple, cherry] (우연)
HashSet null 허용 new HashSet<>(Arrays.asList(null, 1)) [null, 1]
LinkedHashSet 삽입 순서 new LinkedHashSet<>(List.of(3, 1, 2)) [3, 1, 2]
LinkedHashSet.getFirst/getLast/reversed JDK 21 new LinkedHashSet<>(List.of(3, 1, 2)).reversed() [2, 1, 3]
TreeSet 정렬 (null 불가) new TreeSet<>(List.of(1)).add(null) NullPointerException
TreeSet(Comparator) 정렬 기준 지정 아래 코드 [a, aa, bb, ccc]
TreeSet 비교자 중복 비교 결과 0이면 같은 원소 취급 new TreeSet<>(comparing(String::length)) + bb,aa,c [c, bb] (aa 탈락)
first() / last() 최소/최대 ts.first() + "/" + ts.last() (ts={1,3,5,7,9}) 1/9
floor(x) / ceiling(x) ≤x 최대 / ≥x 최소 ts.floor(4) + "/" + ts.ceiling(4) 3/5
lower(x) / higher(x) <x 최대 / >x 최소 ts.lower(5) + "/" + ts.higher(5) 3/7
headSet(x) / headSet(x, incl) x 미만 / 이하 ts.headSet(5) / ts.headSet(5, true) [1, 3] / [1, 3, 5]
tailSet(x) / tailSet(x, incl) x 이상 / 초과 ts.tailSet(5) / ts.tailSet(5, false) [5, 7, 9] / [7, 9]
subSet(a, b) [a, b) ts.subSet(3, 7) [3, 5]
descendingSet() 역순 뷰 ts.descendingSet() [9, 7, 5, 3, 1]
pollFirst() / pollLast() 꺼내며 삭제 t.pollFirst(); t.pollLast() (t={1,2,3}) [2]

TreeSet(Comparator) 정렬 기준 예제(bb, a, ccc, aa 삽입)입니다.

java
new TreeSet<>(comparing(String::length).thenComparing(naturalOrder()))
  • Set에 넣는 객체는 equals/hashCode(HashSet) 또는 compareTo(TreeSet)가 올바르게 정의되어야 한다. 넣은 뒤 그 필드를 바꾸면 contains가 실패한다(실수 7).
05Map
메서드/시그니처 설명 한 줄 예제 (m = {a=1, b=2}) 결과
put(K, V) 삽입/교체, 이전 값 반환 m.put("a", 10) / m.put("c", 3) 1 / null
get(K) 없으면 null m.get("zzz") null
getOrDefault(K, V) 기본값 m.getOrDefault("zzz", 0) 0
containsKey(K) / containsValue(V) 존재 m.containsKey("a") true
putIfAbsent(K, V) 없을 때만 삽입, 기존 값 반환 m.putIfAbsent("a", 99) / ("d", 4) 1 / null
computeIfAbsent(K, f) 없으면 f(k) 저장 후 값 반환 g.computeIfAbsent("x", k -> new ArrayList<>()).add(1) ×2 {x=[1, 2]}
computeIfPresent(K, f) 있을 때만 갱신 g.computeIfPresent("a", (k, v) -> v + 10) {a=11}
compute(K, f) 항상 갱신 (v는 null 가능) g.compute("a", (k, v) -> v == null ? 1 : v + 1) ×2 {a=2}
compute → null null 반환 시 삭제 g.compute("a", (k, v) -> null) {}
merge(K, V, f) 없으면 V, 있으면 f(old, V) g.merge("a", 1, Integer::sum) ×2 {a=2}
merge 문자열 이어 붙이기 g.merge("a","x",String::concat); g.merge("a","y",String::concat) {a=xy}
remove(K) 삭제, 값 반환 g.remove("a") / g.remove("zz") 1 / null
remove(K, V) 값도 일치할 때만 g.remove("a", 2) / g.remove("a", 1) false / true
replace(K, V) 있을 때만 교체 g.replace("a", 5) / g.replace("z", 5) 1 / null
replaceAll(BiFunction) 전체 값 변환 g.replaceAll((k, v) -> v * 100) {a=100, b=200}
putAll(Map) 병합 g.putAll(Map.of("b", 2)) {a=1, b=2}
keySet() / values() / entrySet() 뷰 new TreeMap<>(m).keySet() [a, b]
entry.getKey() / getValue() / setValue() 엔트리 조작 for (var e : g.entrySet()) e.setValue(e.getValue() * 2) {a=2}
keySet().remove(k) 뷰를 통한 삭제 g.keySet().remove("a"); g.values().removeIf(v -> v == 2) {}
entrySet().removeIf(...) 조건 삭제 g.entrySet().removeIf(e -> e.getValue() == 1) {b=2}
forEach(BiConsumer) 순회 m.forEach((k, v) -> System.out.println(k + "=" + v)) a=1 b=2
size() / isEmpty() / clear() 기본 Map.of().isEmpty() true
Map.entry(k, v) 엔트리 생성 Map.entry("k", 1) k=1
HashMap null 키 허용 (1개) new HashMap<>() {{ put(null, 1); }} {null=1}
TreeMap null 키 불가 new TreeMap<>().put(null, 1) NullPointerException
Map.of null 키/값 모두 불가 Map.of("a", null) NullPointerException
HashMap 순서 보장 없음 new HashMap<>(Map.of("banana",1,"apple",2,"cherry",3)) {banana=1, apple=2, cherry=3} (우연)
LinkedHashMap 삽입 순서 put banana, apple, cherry {banana=1, apple=2, cherry=3}
LinkedHashMap(cap, lf, true) 접근 순서 (LRU용) 스니펫 5 참고
LinkedHashMap.firstEntry/lastEntry/reversed JDK 21 l.firstEntry() + "/" + l.lastEntry() a=1/b=2
LinkedHashMap.putFirst/putLast 위치 지정 (JDK 21) l.putFirst("z", 0); l.putLast("a", 9) {z=0, a=9}
TreeMap 순서 키 정렬 new TreeMap<>(Map.of("banana",1,"apple",2,"cherry",3)) {apple=2, banana=1, cherry=3}
TreeMap(String.CASE_INSENSITIVE_ORDER) 대소문자 무시 키 put B=1, a=2 {a=2, B=1}
firstKey() / lastKey() 최소/최대 키 tm.firstKey() + "/" + tm.lastKey() (tm={1=a,3=c,5=e}) 1/5
firstEntry() / lastEntry() 엔트리 tm.firstEntry() 1=a
floorKey(k) / ceilingKey(k) ≤k / ≥k tm.floorKey(4) + "/" + tm.ceilingKey(4) 3/5
lowerKey(k) / higherKey(k) <k / >k tm.lowerKey(3) + "/" + tm.higherKey(3) 1/5
floorEntry(k) 등 엔트리 버전 tm.floorEntry(4) 3=c
headMap(k) / headMap(k, incl) k 미만/이하 tm.headMap(3) / tm.headMap(3, true) {1=a} / {1=a, 3=c}
tailMap(k) k 이상 tm.tailMap(3) {3=c, 5=e}
subMap(a, b) [a, b) tm.subMap(1, 5) {1=a, 3=c}
descendingMap() 역순 뷰 tm.descendingMap() {5=e, 3=c, 1=a}
pollFirstEntry() 꺼내며 삭제 t.pollFirstEntry() (t={1=a,2=b}) 1=a, 남은 {2=b}
ConcurrentHashMap 스레드 안전, null 불가 new ConcurrentHashMap<>(Map.of("a", 1)) {a=1}
  • Map<String, Integer>에서 int v = m.get("x")는 키가 없으면 언박싱 NPE. getOrDefault나 Integer 타입으로 받는다.
  • computeIfAbsent의 함수가 null을 반환하면 아무것도 넣지 않는다.
06Queue / Deque / PriorityQueue / Stack
메서드/시그니처 설명 한 줄 예제 결과
offer(e) / add(e) 뒤에 추가 (offer는 실패 시 false, add는 예외) q.offer(4) true
peek() / element() 앞 조회 (비면 null / 예외) new ArrayDeque<>().element() NoSuchElementException
poll() / remove() 앞 꺼내기 (비면 null / 예외) new ArrayDeque<>().poll() null
offerFirst / offerLast Deque 양끝 추가 dq.offerFirst(1); dq.offerLast(2) [1, 2]
push(e) = addFirst 스택 push dq.push(0) [0, 1, 2]
pop() = removeFirst 스택 pop (비면 예외) new ArrayDeque<>().pop() NoSuchElementException
peekFirst / peekLast 양끝 조회 dq.peekFirst() + "/" + dq.peekLast() ([0,1,2,3]) 0/3
pollFirst / pollLast 양끝 꺼내기 dq.pollFirst(); dq.pollLast(); dq.pop() 남은 [2]
descendingIterator() 역순 순회 new ArrayDeque<>(List.of(1,2,3)).descendingIterator() 3, 2, 1
reversed() 역순 뷰 (JDK 21) new ArrayDeque<>(List.of(1, 2, 3)).reversed() [3, 2, 1]
ArrayDeque null 불가 new ArrayDeque<Integer>().add(null) NullPointerException
ArrayBlockingQueue.offer 꽉 참 false new ArrayBlockingQueue<>(1) 1개 넣고 offer(2) false
new PriorityQueue<>() 최소 힙 (자연순) pq.poll(); pq.poll() (5,1,3) 1, 3
new PriorityQueue<>(reverseOrder()) 최대 힙 pq2.poll() (5,1,3) 5
PriorityQueue.toString 정렬 순서 아님 (힙 배열) new PriorityQueue<>(List.of(5, 1, 3, 2)) [1, 2, 3, 5] (우연히 정렬처럼 보일 뿐)
Stack.push/pop/peek/search 레거시 스택 (Deque 권장) st.push(1); st.push(2); st.peek() 2
LinkedList as Queue Queue<Integer> q = new LinkedList<>() q.offer(4); q.peek(); q.poll() (1,2,3) 1, 남은 [2, 3, 4]
  • 스택은 Stack 대신 ArrayDeque를 쓴다(push/pop/peek). Stack은 Vector 기반 동기화로 느리고 API가 어색하다.
  • PriorityQueue를 순회하면 정렬 순서가 아니다. 정렬된 순서로 꺼내려면 poll()을 반복한다.
07정렬과 Comparator
메서드/시그니처 설명 한 줄 예제 결과
Collections.sort(list) 자연순 (Comparable) sort(l) (3,1,2) [1, 2, 3]
list.sort(cmp) 비교자 정렬 (안정 정렬) l.sort(Comparator.reverseOrder()) [3, 2, 1]
Comparator.naturalOrder() 자연순 Stream.of("b", "a").sorted(naturalOrder()) [a, b]
Comparator.reverseOrder() 역순 Stream.of("b", "a", "c").sorted(reverseOrder()) [c, b, a]
Comparator.comparing(keyFn) 키 추출 sorted(comparing(Person::name)) [Choi, Kim, Lee, Park]
comparingInt/Long/Double 기본형 키 (박싱 없음) sorted(comparingInt(Person::age)) [Lee, Choi, Kim, Park]
thenComparing(...) 2차 정렬 comparing(Person::age).thenComparing(Person::name) [Choi25, Lee25, Kim30, Park30]
.reversed() 체인 전체 역순 (위치 주의) comparing(Person::age).reversed().thenComparing(Person::name) [Kim30, Park30, Choi25, Lee25]
comparing(keyFn, cmp) 키별 방향 지정 아래 코드 [Park30, Kim30, Lee25, Choi25]
comparing(String::length, reverseOrder()) 길이 내림차순 Stream.of("bb","a","ccc").sorted(...) [ccc, bb, a]
nullsFirst(cmp) / nullsLast(cmp) null 처리 Stream.of("b", null, "a").sorted(nullsFirst(naturalOrder())) [null, a, b]
String.CASE_INSENSITIVE_ORDER 대소문자 무시 Stream.of("b", "A", "c").sorted(CASE_INSENSITIVE_ORDER) [A, b, c]
기본 문자열 정렬 대문자 먼저 Stream.of("b", "A", "c").sorted() [A, b, c]
람다 비교자 직접 작성 (a, b) -> a.length() - b.length() (오버플로 주의: Integer.compare 권장)
Collections.reverseOrder() = Comparator.reverseOrder() l.sort(Collections.reverseOrder()) [3, 2, 1]
Arrays.sort(arr, cmp) 객체 배열 정렬 Arrays.sort(a, Collections.reverseOrder()) (Integer[]) [5, 3, 1]
int[] 내림차순 기본형은 cmp 불가 → boxed 아래 코드 [5, 3, 1]

comparing(keyFn, cmp)와 int[] 내림차순 예제 코드입니다.

java
comparing(Person::age, reverseOrder()).thenComparing(Person::name, reverseOrder())
IntStream.of(5, 3, 1).boxed().sorted(reverseOrder()).mapToInt(Integer::intValue).toArray()
  • reversed()는 그 앞까지의 비교자 전체를 뒤집는다. comparing(age).thenComparing(name).reversed()는 둘 다 역순.
  • Comparator.comparing(Person::name)처럼 메서드 참조가 타입 추론에 실패하면 Comparator.<Person, String>comparing(...) 또는 comparing((Person p) -> p.name()).
08검색
메서드/시그니처 설명 한 줄 예제 결과
list.contains(x) 선형 O(n) List.of(3, 1, 4).contains(4) true
set.contains(x) 해시 O(1) Set.of(3, 1, 4).contains(4) true
list.indexOf(x) 첫 위치 List.of(3, 1, 4, 1).indexOf(1) 1
Collections.binarySearch(list, key) 정렬된 리스트 이진 검색 binarySearch(List.of(1, 3, 5, 7), 5) 2
binarySearch 미존재 -(삽입점) - 1 binarySearch(List.of(1, 3, 5, 7), 4) -3 (삽입점 2)
Arrays.binarySearch(arr, key) 정렬된 배열 Arrays.binarySearch(new int[]{1,2,3,4,5}, 4) 3
Collections.indexOfSubList(src, target) 부분 리스트 위치 indexOfSubList(List.of(1,2,3,4), List.of(3,4)) 2
Collections.max(coll) / min 최대/최소 Collections.max(List.of(3, 9, 2)) 9
Collections.max(coll, cmp) 기준 지정 max(List.of("bb","a","ccc"), comparing(String::length)) ccc
Collections.frequency(coll, x) 출현 횟수 frequency(List.of(1, 1, 2), 1) 2
stream().filter().findFirst() 조건 검색 l.stream().filter(x -> x > 2).findFirst() Optional[3]
stream().anyMatch(p) 존재 여부 IntStream.of(1, 2, 3).anyMatch(x -> x == 2) true
Arrays.asList(arr).contains(x) 배열 포함 검사 (객체 배열) Arrays.asList(1, 2, 3).contains(2) true
TreeSet.floor/ceiling 근접 값 검색 4절 참고
09변환 (list ↔ array ↔ stream ↔ map)
변환 한 줄 예제 결과
int[] → List<Integer> Arrays.stream(arr).boxed().toList() [5, 3, 1, 4, 2]
List<Integer> → int[] list.stream().mapToInt(Integer::intValue).toArray() [1, 2]
String[] → List<String> (가변) new ArrayList<>(Arrays.asList(arr)) [a, b]
String[] → List<String> (불변) List.of(arr) [a, b]
List<String> → String[] list.toArray(String[]::new) String[]
List → Set (중복 제거) new TreeSet<>(Arrays.asList(3, 1, 3)) [1, 3]
Set → List new ArrayList<>(set) 리스트
List → Map list.stream().collect(toMap(s -> s, String::length)) {bb=2, a=1}
Map → List<String> map.entrySet().stream().map(e -> e.getKey()+e.getValue()).toList() [a1, b2]
Map → 정렬된 키 리스트 new TreeSet<>(map.keySet()) [a, b]
Map 뒤집기 (값→키) 아래 코드 1 {2=b, 3=c, 4=d, 10=a}
Map 값 기준 정렬 아래 코드 2 [a=10, d=4, c=3, b=2]
Map 정렬 → LinkedHashMap 아래 코드 3 {b=2, c=3, d=4, a=10}
char[] 정렬 → String char[] c = s.toCharArray(); Arrays.sort(c); new String(c) "abcd"
Iterator → List it.forEachRemaining(out::add) 리스트
Enumeration → List Collections.list(new StringTokenizer("a b c")) 크기 3
Stream → 컬렉션 지정 stream.collect(Collectors.toCollection(TreeSet::new)) TreeSet

Map 뒤집기·값 정렬·LinkedHashMap 변환 코드입니다.

java
// 1: 값→키로 뒤집기 (키 중복 시 첫 값 유지)
m.entrySet().stream().collect(toMap(
    Map.Entry::getValue, Map.Entry::getKey, (a, b) -> a, TreeMap::new));

// 2: 값 기준 내림차순 정렬
m.entrySet().stream().sorted(Map.Entry.comparingByValue(reverseOrder()));

// 3: 정렬 결과를 LinkedHashMap으로 (순서 유지)
.sorted(comparingByValue()).collect(toMap(
    getKey, getValue, (a, b) -> a, LinkedHashMap::new));
10Collections 유틸
메서드/시그니처 설명 한 줄 예제 결과
sort(list) / sort(list, cmp) 정렬 sort(l) (3,1,2) [1, 2, 3]
reverse(list) 뒤집기 (제자리) reverse(l) (1,2,3) [3, 2, 1]
shuffle(list) / shuffle(list, rnd) 섞기 (시드로 재현) shuffle(l, new Random(42)) (1..5) [2, 3, 4, 5, 1]
swap(list, i, j) 교환 swap(l, 0, 2) (1,2,3) [3, 2, 1]
rotate(list, d) 회전 rotate(l, 1) (1,2,3,4) [4, 1, 2, 3]
fill(list, x) 전부 채움 fill(l, 0) (1,2,3) [0, 0, 0]
frequency(coll, x) 출현 횟수 frequency(List.of(1, 1, 2), 1) 2
max(coll) / min(coll) 최대/최소 max(List.of(3, 9, 2)) + "/" + min(...) 9/2
addAll(coll, e...) 가변 인자 추가 addAll(l, 1, 2, 3) [1, 2, 3]
nCopies(n, x) 불변 반복 nCopies(2, "ab") [ab, ab]
emptyList/emptySet/emptyMap() 빈 불변 emptyMap() + " " + emptySet() {} []
singletonList/singleton/singletonMap 1개짜리 불변 singletonMap("k", 1) {k=1}
unmodifiableList/Set/Map(c) 읽기 전용 뷰 unmodifiableList(new ArrayList<>()).add(1) UnsupportedOperationException
synchronizedList/Map(c) 동기화 래퍼 (순회는 수동 lock) synchronizedList(new ArrayList<>()) SynchronizedRandomAccessList
binarySearch(list, key) 정렬 리스트 검색 binarySearch(List.of(1,3,5,7), 5) 2
disjoint(a, b) 교집합 없음 disjoint(List.of(1, 2), List.of(3)) true
indexOfSubList(src, t) 부분 리스트 위치 indexOfSubList(List.of(1,2,3,4), List.of(3,4)) 2
reverseOrder() 역순 비교자 l.sort(Collections.reverseOrder()) [3, 2, 1]
list(Enumeration) Enumeration → ArrayList Collections.list(new StringTokenizer("a b c")).size() 3
11Arrays 유틸 전체
메서드/시그니처 설명 한 줄 예제 (arr = {5, 3, 1, 4, 2}) 결과
toString(arr) 1차원 출력 Arrays.toString(arr) [5, 3, 1, 4, 2]
deepToString(arr2d) 다차원 출력 Arrays.deepToString(new int[][]{{1, 2}, {3}}) [[1, 2], [3]]
sort(arr) 오름차순 (기본형 DualPivotQuickSort) Arrays.sort(a) [1, 2, 3, 4, 5]
sort(arr, from, to) 구간 정렬 Arrays.sort(a, 1, 4) [5, 1, 3, 4, 2]
sort(T[], cmp) 객체 배열 + 비교자 (TimSort, 안정) Arrays.sort(a, Collections.reverseOrder()) (Integer[]{5,3,1}) [5, 3, 1]
parallelSort(arr) 병렬 정렬 (큰 배열) Arrays.parallelSort(a) 정렬됨
binarySearch(arr, key) 정렬된 배열 검색 Arrays.binarySearch(sorted, 4) / (sorted, 6) 3 / -6
fill(arr, x) 채우기 Arrays.fill(new int[3], 7) [7, 7, 7]
setAll(arr, i -> ...) 인덱스 기반 초기화 Arrays.setAll(a, i -> i * i) (길이 4) [0, 1, 4, 9]
copyOf(arr, n) 길이 n으로 복사 (부족분 0) Arrays.copyOf(arr, 7) / (arr, 2) [5,3,1,4,2,0,0] / [5, 3]
copyOfRange(arr, a, b) [a, b) 복사 Arrays.copyOfRange(arr, 1, 3) [3, 1]
arr.clone() 얕은 복사 arr.clone() [5, 3, 1, 4, 2]
System.arraycopy(src, sp, dst, dp, len) 구간 복사 (가장 빠름) arraycopy({1,2,3}, 0, new int[5], 1, 3) [0, 1, 2, 3, 0]
equals(a, b) 1차원 내용 비교 Arrays.equals(new int[]{1,2}, new int[]{1,2}) true
arr == arr2 / arr.equals(arr2) 참조 비교 new int[]{1,2}.equals(new int[]{1,2}) false
deepEquals(a, b) 다차원 비교 Arrays.deepEquals(new int[][]{{1}}, new int[][]{{1}}) true (equals는 false)
hashCode(arr) 내용 해시 Arrays.hashCode(new int[]{1, 2}) 994
asList(T...) 고정 크기 리스트 뷰 Arrays.asList("a", "b") [a, b]
stream(arr) / stream(arr, a, b) 스트림 Arrays.stream(arr).sum() / stream(arr, 1, 3).sum() 15 / 4
mismatch(a, b) 첫 불일치 인덱스 (JDK 9+) Arrays.mismatch(new int[]{1,2,3}, new int[]{1,9,3}) 1
compare(a, b) 사전순 비교 (JDK 9+) Arrays.compare(new int[]{1,2}, new int[]{1,3}) -1
parallelPrefix(arr, op) 누적 연산 Arrays.parallelPrefix(a, Integer::sum) (1,2,3,4) [1, 3, 6, 10]
new int[2][3] 2차원 (0 초기화) Arrays.deepToString(new int[2][3]) [[0, 0, 0], [0, 0, 0]]
  • Arrays.asList(int[])는 List<int[]>(원소 1개)가 된다. 기본형 배열은 Arrays.stream(arr).boxed().toList().
  • Arrays.sort(int[])에는 Comparator를 줄 수 없다. 내림차순은 boxed() 후 정렬하거나, 오름차순 후 뒤집는다.
12시간 복잡도
자료구조 get(i) add(끝) add(중간) remove contains 순서 null
ArrayList O(1) O(1)* O(n) O(n) O(n) 삽입 순 허용
LinkedList O(n) O(1) O(n) 탐색 + O(1) O(n) 탐색 + O(1) O(n) 삽입 순 허용
ArrayDeque - O(1) 양끝 - O(1) 양끝 O(n) 삽입 순 불가
PriorityQueue - O(log n) - O(log n) poll O(n) 힙 (우선순위) 불가
HashSet / HashMap - O(1)* - O(1)* O(1)* 없음 허용 (키 1개)
LinkedHashSet / LinkedHashMap - O(1)* - O(1)* O(1)* 삽입 (또는 접근) 순 허용
TreeSet / TreeMap - O(log n) - O(log n) O(log n) 정렬 순 불가
ConcurrentHashMap - O(1)* - O(1)* O(1)* 없음 불가
CopyOnWriteArrayList O(1) O(n) 복사 O(n) O(n) O(n) 삽입 순 허용

* 분할 상환(amortized) 또는 해시 충돌 없을 때 평균.

  • 선택 기준: 인덱스 접근 → ArrayList. 양끝 큐/스택 → ArrayDeque. 중복 제거 → HashSet. 정렬 유지·범위 검색 → TreeMap/TreeSet. 순서 유지 Map → LinkedHashMap. 멀티스레드 → ConcurrentHashMap.
13Iterator 안전 삭제
방법 설명 한 줄 예제 (l = [1, 2, 3]) 결과
for-each 안에서 list.remove ConcurrentModificationException (실수 3 참고) for (Integer x : l) if (x == 1) l.remove(x) ConcurrentModificationException
Iterator.remove() 안전 아래 코드 1 [1, 3]
removeIf(Predicate) 가장 간단 (권장) l.removeIf(x -> x == 2) [1, 3]
역순 인덱스 루프 인덱스 밀림 방지 아래 코드 2 [1, 3]
entrySet().removeIf Map 조건 삭제 g.entrySet().removeIf(e -> e.getValue() == 1) {b=2}
CopyOnWriteArrayList 순회 중 수정 허용 (스냅샷 순회) for (Integer x : cow) if (x == 2) cow.remove(x) [1, 3]
Iterator.forEachRemaining 남은 원소 처리 it.next(); it.forEachRemaining(out::add) [2, 3]

Iterator.remove()와 역순 인덱스 루프 코드입니다.

java
// 1: Iterator.remove()로 순회 중 안전하게 삭제
var it = l.iterator();
while (it.hasNext()) if (it.next() == 2) it.remove();

// 2: 역순 인덱스 루프로 인덱스 밀림 방지
for (int i = l.size() - 1; i >= 0; i--) if (l.get(i) == 2) l.remove(i);
  • for-each 순회 중 삭제가 뒤에서 두 번째 원소일 때는 예외가 안 나고 조용히 끝난다(hasNext가 false가 됨). 예외가 안 났다고 안전한 게 아니다.
14실무 스니펫

스니펫 1: 그룹핑 (부서별 이름 목록)

키로 묶어 Map<K, List<V>>를 만든다. computeIfAbsent가 수동 방식의 핵심이고, 스트림은 groupingBy. 정렬된 키가 필요하면 TreeMap::new를 넘긴다.

java
record Person(String name, int age, String city) {}
List<Person> people = List.of(new Person("Kim", 30, "Seoul"), new Person("Lee", 25, "Busan"),
                              new Person("Park", 30, "Seoul"), new Person("Choi", 25, "Seoul"));

// 수동
Map<String, List<String>> byCity = new TreeMap<>();
for (Person p : people) byCity.computeIfAbsent(p.city(), k -> new ArrayList<>()).add(p.name());
// 출력: {Busan=[Lee], Seoul=[Kim, Park, Choi]}

// 스트림
people.stream().collect(Collectors.groupingBy(Person::city, TreeMap::new,
        Collectors.mapping(Person::name, Collectors.toList())));
// 출력: {Busan=[Lee], Seoul=[Kim, Park, Choi]}

스니펫 2: 카운팅 (단어 빈도)

merge(k, 1, Integer::sum)이 카운팅의 표준 관용구다. getOrDefault+put보다 짧고 해시 조회가 1번이다.

java
Map<String, Integer> count = new TreeMap<>();
for (String w : "a b a c b a".split(" ")) count.merge(w, 1, Integer::sum);
// 출력: {a=3, b=2, c=1}

// 스트림 버전
Stream.of("a b a c b a".split(" "))
      .collect(Collectors.groupingBy(w -> w, TreeMap::new, Collectors.counting()));
// 출력: {a=3, b=2, c=1}   (값 타입은 Long)

스니펫 3: 중복 제거, 순서 유지 (+ 특정 키 기준)

LinkedHashSet은 첫 등장 순서를 유지한 채 중복을 없앤다. 객체를 특정 필드 기준으로 중복 제거하려면 seen.add(key)의 반환값을 필터로 쓴다.

java
new ArrayList<>(new LinkedHashSet<>(List.of(3, 1, 3, 2, 1)));
// 출력: [3, 1, 2]
List.of(3, 1, 3, 2, 1).stream().distinct().toList();
// 출력: [3, 1, 2]

// 나이(age)가 같은 사람은 처음 한 명만
Set<Integer> seen = new HashSet<>();
people.stream().filter(p -> seen.add(p.age())).map(Person::name).toList();
// 출력: [Kim, Lee]

스니펫 4: 두 리스트의 차집합 / 교집합 / 합집합 / 대칭차

removeAll/retainAll은 원본을 바꾸므로 복사본에서 실행한다. 큰 리스트끼리는 removeAll(list)이 O(n·m)이라 상대편을 HashSet으로 감싼다.

java
List<Integer> a = List.of(1, 2, 3, 4), b = List.of(3, 4, 5);

var diff = new ArrayList<>(a); diff.removeAll(b);          // 출력: [1, 2]
var inter = new ArrayList<>(a); inter.retainAll(b);        // 출력: [3, 4]
var union = new LinkedHashSet<>(a); union.addAll(b);       // 출력: [1, 2, 3, 4, 5]

var sym = new TreeSet<>(a); sym.addAll(b);
var both = new TreeSet<>(a); both.retainAll(b);
sym.removeAll(both);                                       // 출력: [1, 2, 5]

// 대용량: Set으로 O(n + m)
Set<Integer> bSet = new HashSet<>(b);
a.stream().filter(x -> !bSet.contains(x)).toList();        // 출력: [1, 2]

스니펫 5: LRU 캐시 (LinkedHashMap 접근 순서)

세 번째 생성자 인자 accessOrder=true로 만들면 get할 때마다 그 엔트리가 뒤로 이동한다. removeEldestEntry를 오버라이드해 용량 초과 시 가장 오래된 것을 지운다.

java
static class LruCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;
    LruCache(int capacity) { super(16, 0.75f, true); this.capacity = capacity; }
    @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; }
}

LruCache<String, Integer> c = new LruCache<>(2);
c.put("a", 1); c.put("b", 2);
c.get("a");          // a가 최근 사용으로 이동
c.put("c", 3);       // 용량 초과 → 가장 오래된 b 제거
// 출력: {a=1, c=3}

스니펫 6: 페이징 subList와 청크 분할

subList(from, to)의 인덱스가 크기를 넘으면 예외이므로 Math.min으로 잘라낸다. 마지막 페이지 이후를 요청하면 빈 리스트. 청크 분할은 배치 처리에서 N건씩 끊어 처리할 때 쓴다.

java
static <T> List<T> page(List<T> list, int page, int size) {
    int from = Math.min(page * size, list.size());
    int to = Math.min(from + size, list.size());
    return list.subList(from, to);
}
static <T> List<List<T>> chunk(List<T> list, int size) {
    List<List<T>> out = new ArrayList<>();
    for (int i = 0; i < list.size(); i += size)
        out.add(list.subList(i, Math.min(i + size, list.size())));
    return out;
}

List<Integer> data = IntStream.rangeClosed(1, 23).boxed().toList();
page(data, 0, 10);   // 출력: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
page(data, 2, 10);   // 출력: [21, 22, 23]
page(data, 3, 10);   // 출력: []
int totalPages = (data.size() + 10 - 1) / 10;   // 출력: 3
chunk(List.of(1, 2, 3, 4, 5), 2);               // 출력: [[1, 2], [3, 4], [5]]

스니펫 7: 상위 N개와 Map 값 기준 정렬

정렬 후 limit. Map을 값 기준으로 정렬한 결과는 순서를 유지하는 LinkedHashMap으로 모아야 한다(toMap 기본은 HashMap이라 순서가 사라진다).

java
people.stream().sorted(Comparator.comparingInt(Person::age).reversed()).limit(2).map(Person::name).toList();
// 출력: [Kim, Park]

Map<String, Integer> m = new HashMap<>(Map.of("a", 10, "b", 2, "c", 3, "d", 4));
m.entrySet().stream()
 .sorted(Map.Entry.comparingByValue())
 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> x, LinkedHashMap::new));
// 출력: {b=2, c=3, d=4, a=10}
15자주 하는 실수

❌ 실수 1: List<Integer>.remove(1)이 값을 지운다고 착각

java
var l = new ArrayList<>(List.of(10, 20, 30));
l.remove(1);                    // 인덱스 1 삭제 → [10, 30]
l.remove(Integer.valueOf(10));  // 값 10 삭제

❌ 실수 2: 불변 컬렉션 수정 / Arrays.asList에 add

코드 결과
List.of(1).add(2) UnsupportedOperationException
Arrays.asList(1, 2).add(3) UnsupportedOperationException
Stream.of(1).toList().add(2) UnsupportedOperationException
Collections.unmodifiableList(l).add(1) UnsupportedOperationException
new ArrayList<>(List.of(1)).add(2) OK → [1, 2]

❌ 실수 3: for-each 안에서 컬렉션 수정

java
List<Integer> l = new ArrayList<>(List.of(1, 2, 3));
for (Integer x : l) if (x == 1) l.remove(x);   // ConcurrentModificationException
for (Integer x : l) if (x == 2) l.remove(x);   // 예외 없이 [1, 3] — 뒤에서 두 번째라 우연히 통과 (버그 은닉)
l.removeIf(x -> x == 1);                        // 올바름

❌ 실수 4: Integer == Integer 비교

java
Integer x = 127, y = 127;   x == y;   // true  (-128~127 캐시)
Integer z = 128, w = 128;   z == w;   // false (새 객체)
z.equals(w);                          // true  ← 항상 equals

❌ 실수 5: Map.get 결과를 int로 받아 NPE

java
Map<String, Integer> g = new HashMap<>();
int v = g.get("x");                  // NullPointerException (null 언박싱)
int v = g.getOrDefault("x", 0);      // 0
Integer v = g.get("x");              // null 체크 가능

❌ 실수 6: HashMap 순서에 의존

java
new HashMap<>(Map.of("banana", 1, "apple", 2, "cherry", 3));  // {banana=1, apple=2, cherry=3} 우연
// 삽입 순서 필요 → LinkedHashMap, 정렬 필요 → TreeMap. HashMap 순서는 리해시 시 바뀔 수 있다.

❌ 실수 7: Set/Map 키로 쓴 객체를 나중에 변경

java
Set<List<Integer>> s = new HashSet<>();
List<Integer> k = new ArrayList<>(List.of(1));
s.add(k);
k.add(2);              // 해시가 바뀜
s.contains(k);         // false — 들어 있는데 못 찾음 (size는 1)
// 키는 불변 객체(String, record, Integer)로.

❌ 실수 8: List.equals는 순서를 본다, Set.equals는 안 본다

java
List.of(1, 2).equals(List.of(2, 1));   // false
Set.of(1, 2).equals(Set.of(2, 1));     // true

❌ 실수 9: PriorityQueue를 순회하면 정렬돼 있을 거라 기대

java
PriorityQueue<Integer> pq = new PriorityQueue<>(List.of(5, 1, 3, 2));
pq.toString();          // [1, 2, 3, 5] — 우연. 큰 데이터에선 힙 배열 순서라 뒤섞임
while (!pq.isEmpty()) pq.poll();   // 정렬된 순서로 꺼내는 유일한 방법

❌ 실수 10: Arrays.equals 대신 ==, 다차원에 equals

java
new int[]{1, 2} == new int[]{1, 2};                            // false
Arrays.equals(new int[]{1, 2}, new int[]{1, 2});               // true
Arrays.equals(new int[][]{{1}}, new int[][]{{1}});             // false (내부 배열 참조 비교)
Arrays.deepEquals(new int[][]{{1}}, new int[][]{{1}});         // true
16관련 레슨
  • 중급 05 컬렉션 프레임워크 딥다이브: ArrayList/LinkedList/HashMap/TreeMap 내부 구조, Iterator, Comparator, 시간 복잡도
  • 초급 03 1차원·2차원 배열: 배열 기초, Arrays.sort/copyOf
  • 중급 01 상속·다형성: equals/hashCode 계약 (HashSet/HashMap 키의 조건)
  • 고급 01 제네릭과 와일드카드: List<? extends T>, Comparator<? super T>
  • 고급 04 스트림: groupingBy, toMap, Collectors
  • 고급 02 멀티스레드: ConcurrentHashMap, CopyOnWriteArrayList, synchronizedList
  • 배치 03 청크 처리: subList 청크 분할 활용
목차
  • 한눈에 보기
  • 생성: 불변 vs 가변
  • List
  • Set
  • Map
  • Queue / Deque / PriorityQueue / Stack
  • 정렬과 Comparator
  • 검색
  • 변환 (list ↔ array ↔ stream ↔ map)
  • Collections 유틸
  • Arrays 유틸 전체
  • 시간 복잡도
  • Iterator 안전 삭제
  • 실무 스니펫
  • 자주 하는 실수
  • 관련 레슨
참고 영상 · 인터넷 연결 시 유튜브 검색이 열립니다자바 Collections · Arrays · Map명령어 List Set Map
이전01 String · StringBuilder · 정규식다음03 Stream · Collectors · 함수형 인터페이스