List/Set/Map/Queue/Deque의 주요 메서드, 생성 방법, 정렬·검색·변환,
Collections/Arrays유틸을 표로 정리했다. 원리는 중급 05 레슨에서 다루고, 여기서는 어떤 메서드가 있고 결과가 무엇인지만 빠르게 찾는다.Ctrl+F로 메서드명(예:computeIfAbsent,subList,binarySearch)을 검색하면 된다. 모든 결과는 JDK 21 실행값이다.
| 메서드 | 설명 | 예제 | 결과 |
|---|---|---|---|
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] |
| 메서드/시그니처 | 설명 | 한 줄 예제 | 결과 |
|---|---|---|---|
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>)로, 구현체는 생성 시점에만 명시한다.| 메서드/시그니처 | 설명 | 한 줄 예제 (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.| 메서드/시그니처 | 설명 | 한 줄 예제 | 결과 |
|---|---|---|---|
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 삽입)입니다.
new TreeSet<>(comparing(String::length).thenComparing(naturalOrder()))equals/hashCode(HashSet) 또는 compareTo(TreeSet)가 올바르게 정의되어야 한다. 넣은 뒤 그 필드를 바꾸면 contains가 실패한다(실수 7).| 메서드/시그니처 | 설명 | 한 줄 예제 (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을 반환하면 아무것도 넣지 않는다.| 메서드/시그니처 | 설명 | 한 줄 예제 | 결과 |
|---|---|---|---|
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()을 반복한다.| 메서드/시그니처 | 설명 | 한 줄 예제 | 결과 |
|---|---|---|---|
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[] 내림차순 예제 코드입니다.
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()).| 메서드/시그니처 | 설명 | 한 줄 예제 | 결과 |
|---|---|---|---|
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절 참고 |
| 변환 | 한 줄 예제 | 결과 |
|---|---|---|
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 변환 코드입니다.
// 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));| 메서드/시그니처 | 설명 | 한 줄 예제 | 결과 |
|---|---|---|---|
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 |
| 메서드/시그니처 | 설명 | 한 줄 예제 (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() 후 정렬하거나, 오름차순 후 뒤집는다.| 자료구조 | 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.| 방법 | 설명 | 한 줄 예제 (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()와 역순 인덱스 루프 코드입니다.
// 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);hasNext가 false가 됨). 예외가 안 났다고 안전한 게 아니다.키로 묶어 Map<K, List<V>>를 만든다. computeIfAbsent가 수동 방식의 핵심이고, 스트림은 groupingBy. 정렬된 키가 필요하면 TreeMap::new를 넘긴다.
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]}merge(k, 1, Integer::sum)이 카운팅의 표준 관용구다. getOrDefault+put보다 짧고 해시 조회가 1번이다.
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)LinkedHashSet은 첫 등장 순서를 유지한 채 중복을 없앤다. 객체를 특정 필드 기준으로 중복 제거하려면 seen.add(key)의 반환값을 필터로 쓴다.
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]removeAll/retainAll은 원본을 바꾸므로 복사본에서 실행한다. 큰 리스트끼리는 removeAll(list)이 O(n·m)이라 상대편을 HashSet으로 감싼다.
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]세 번째 생성자 인자 accessOrder=true로 만들면 get할 때마다 그 엔트리가 뒤로 이동한다. removeEldestEntry를 오버라이드해 용량 초과 시 가장 오래된 것을 지운다.
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}subList(from, to)의 인덱스가 크기를 넘으면 예외이므로 Math.min으로 잘라낸다. 마지막 페이지 이후를 요청하면 빈 리스트. 청크 분할은 배치 처리에서 N건씩 끊어 처리할 때 쓴다.
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]]정렬 후 limit. Map을 값 기준으로 정렬한 결과는 순서를 유지하는 LinkedHashMap으로 모아야 한다(toMap 기본은 HashMap이라 순서가 사라진다).
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}List<Integer>.remove(1)이 값을 지운다고 착각var l = new ArrayList<>(List.of(10, 20, 30));
l.remove(1); // 인덱스 1 삭제 → [10, 30]
l.remove(Integer.valueOf(10)); // 값 10 삭제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] |
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); // 올바름Integer == Integer 비교Integer x = 127, y = 127; x == y; // true (-128~127 캐시)
Integer z = 128, w = 128; z == w; // false (새 객체)
z.equals(w); // true ← 항상 equalsMap.get 결과를 int로 받아 NPEMap<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 체크 가능HashMap 순서에 의존new HashMap<>(Map.of("banana", 1, "apple", 2, "cherry", 3)); // {banana=1, apple=2, cherry=3} 우연
// 삽입 순서 필요 → LinkedHashMap, 정렬 필요 → TreeMap. HashMap 순서는 리해시 시 바뀔 수 있다.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)로.List.equals는 순서를 본다, Set.equals는 안 본다List.of(1, 2).equals(List.of(2, 1)); // false
Set.of(1, 2).equals(Set.of(2, 1)); // truePriorityQueue를 순회하면 정렬돼 있을 거라 기대PriorityQueue<Integer> pq = new PriorityQueue<>(List.of(5, 1, 3, 2));
pq.toString(); // [1, 2, 3, 5] — 우연. 큰 데이터에선 힙 배열 순서라 뒤섞임
while (!pq.isEmpty()) pq.poll(); // 정렬된 순서로 꺼내는 유일한 방법Arrays.equals 대신 ==, 다차원에 equalsnew 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}}); // trueArrays.sort/copyOfequals/hashCode 계약 (HashSet/HashMap 키의 조건)List<? extends T>, Comparator<? super T>groupingBy, toMap, CollectorsConcurrentHashMap, CopyOnWriteArrayList, synchronizedListsubList 청크 분할 활용