알고리즘 Cheat Sheet 시리즈는 코딩 테스트를 풀다가 "이거 자바에선 뭐였지?", "파이썬은 어떻게 했더라?" 싶을 때 바로 펼쳐 보려고 만든 개인 참고용 정리입니다.
Java와 Python을 나란히 놓고, 문법 차이 때문에 실수하기 쉬운 부분만 짧게 정리합니다.

이번 주제는 람다입니다!
람다는 쉽게 말하면 이름 없는 짧은 함수입니다. 코테에서는 주로 정렬 기준, 변환(map)/거르기(filter), 입력 파싱에 씁니다.
| 형태 | 자바 | 파이썬 |
|---|---|---|
| 인자 1개 | x -> x * 2 | lambda x: x * 2 |
| 인자 2개 | (a, b) -> a + b | lambda a, b: a + b |
| 인자 없음 | () -> 42 | lambda: 42 |
| 여러 줄 | (a, b) -> { int s = a + b; return s * 2; } | 불가능 (식 하나만) |
| 조건 분기 | x -> x % 2 == 0 ? "짝" : "홀" | lambda x: "짝" if x % 2 == 0 else "홀" |
// 자바 람다는 반드시 "함수형 인터페이스" 타입에 담아야 한다
Function<Integer, Integer> doubleIt = x -> x * 2;
BinaryOperator<Integer> add = (a, b) -> a + b;
doubleIt.apply(3); // 6
add.apply(2, 3); // 5
double_it = lambda x: x * 2
add = lambda a, b: a + b
double_it(3) # 6
add(2, 3) # 5
파이썬 람다는 식(expression) 하나만 쓸 수 있다.
if문,for문, 대입이 필요하면def로 함수를 만든다.
자바 람다는{ }블록을 쓰면 여러 줄도 가능하지만, 이때는return을 직접 써야 한다.
자바 람다는 혼자 존재할 수 없고, 어떤 인터페이스의 구현으로 쓰인다. 자주 보는 것만 알아두면 된다.
| 인터페이스 | 모양 | 호출 | 주로 쓰는 곳 |
|---|---|---|---|
Comparator<T> | (a, b) -> int | compare(a, b) | 정렬 |
Function<T, R> | x -> 결과 | apply(x) | 변환 (map) |
Predicate<T> | x -> boolean | test(x) | 조건 (filter, removeIf) |
Consumer<T> | x -> { } (반환 없음) | accept(x) | forEach |
Supplier<T> | () -> 값 | get() | computeIfAbsent의 새 객체 생성 등 |
BinaryOperator<T> | (a, b) -> 같은 타입 | apply(a, b) | reduce, merge |
람다가 이미 있는 함수를 호출만 할 때는 더 짧게 쓸 수 있다.
| 하고 싶은 일 | 자바 람다 | 자바 메서드 참조 | 파이썬 |
|---|---|---|---|
| 문자열 → 정수 | s -> Integer.parseInt(s) | Integer::parseInt | int |
| 길이 | s -> s.length() | String::length | len |
| 두 수 더하기 | (a, b) -> a + b | Integer::sum | operator.add |
| 출력 | x -> System.out.println(x) | System.out::println | print |
| 새 리스트 생성 | () -> new ArrayList<>() | ArrayList::new | list |
map.merge(key, 1, Integer::sum);
words.sort(Comparator.comparing(String::length));
words.sort(key=len) # 함수 이름을 그대로 넘긴다
nums = list(map(int, ["1", "2"])) # int 함수를 그대로 넘긴다
파이썬은 함수 자체가 값이라서
key=len처럼 괄호 없이 이름만 넘기면 된다.key=len()이라고 쓰면 에러 발생!
| 기준 | 자바 | 파이썬 |
|---|---|---|
| 오름차순 | (a, b) -> Integer.compare(a, b) | 기본값 |
| 내림차순 | (a, b) -> Integer.compare(b, a) | reverse=True |
| 길이순 | Comparator.comparing(String::length) | key=len |
| 길이 내림차순 | Comparator.comparing(String::length).reversed() | key=len, reverse=True |
| 1순위 → 2순위 | .thenComparing(...) | key=lambda x: (x[0], x[1]) |
Comparator.comparing(...)이 파이썬 key=와 같은 방식이라, 익숙해지면 둘을 똑같이 생각할 수 있다.// 파이썬 key=lambda x: (x[0], -x[1]) 과 같은 정렬
Arrays.sort(arr, Comparator.comparingInt((int[] x) -> x[0])
.thenComparing((int[] x) -> x[1], Comparator.reverseOrder()));
자세한 정렬은 #2 형변환/비교/정렬 편 참고
| 기능 | 자바 (스트림) | 파이썬 (함수) | 파이썬 (컴프리헨션) |
|---|---|---|---|
| 모두 변환 | .map(x -> x * 2) | map(lambda x: x * 2, arr) | [x * 2 for x in arr] |
| 조건 거르기 | .filter(x -> x > 0) | filter(lambda x: x > 0, arr) | [x for x in arr if x > 0] |
| 하나로 합치기 | .reduce(0, Integer::sum) | reduce(lambda a, b: a + b, arr, 0) | sum(arr) |
int[] arr = {1, -2, 3, -4, 5};
int[] result = Arrays.stream(arr)
.filter(x -> x > 0) // 1, 3, 5
.map(x -> x * 2) // 2, 6, 10
.toArray();
List<String> words = List.of("apple", "kiwi", "banana");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList()); // [5, 4, 6]
arr = [1, -2, 3, -4, 5]
result = [x * 2 for x in arr if x > 0] # [2, 6, 10] ← 추천
result = list(map(lambda x: x * 2,
filter(lambda x: x > 0, arr))) # 같은 결과
from functools import reduce
total = reduce(lambda a, b: a + b, arr, 0) # 3
파이썬에서는
map+lambda조합보다 리스트 컴프리헨션이 더 읽기 쉽고 흔하다.
단,map(int, ...)처럼 이미 있는 함수를 넘길 때는map이 더 짧다.
| 상황 | 자바 | 파이썬 |
|---|---|---|
| 입력 한 줄 → 정수 배열 | Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray() | list(map(int, input().split())) |
| 가장 긴 문자열 | Collections.max(words, Comparator.comparing(String::length)) | max(words, key=len) |
| 값이 가장 큰 키 | Collections.max(map.entrySet(), Map.Entry.comparingByValue()).getKey() | max(d, key=d.get) |
| 조건에 맞는 개수 | list.stream().filter(x -> x > 0).count() | sum(1 for x in lst if x > 0) |
| 조건에 맞는 원소 삭제 | list.removeIf(x -> x < 0) | lst = [x for x in lst if x >= 0] |
| 모두 변환 (원본 수정) | list.replaceAll(x -> x * 2) | lst = [x * 2 for x in lst] |
| 맵 순회 | map.forEach((k, v) -> ...) | for k, v in d.items(): |
| 하나라도 / 모두 | .anyMatch(x -> x > 0) / .allMatch(...) | any(x > 0 for x in lst) / all(...) |
람다가 쓰는 바깥 지역변수는 사실상 final(한 번도 바뀌지 않는 값)이어야 한다.
int count = 0;
list.forEach(x -> count++); // ❌ 컴파일 에러
int[] count = {0};
list.forEach(x -> count[0]++); // ⭕ 배열 원소는 바꿀 수 있다 (꼼수)
int cnt = 0;
for (int x : list) cnt++; // ⭕ 그냥 for문이 제일 깔끔
funcs = [lambda: i for i in range(3)]
[f() for f in funcs] # [2, 2, 2] ← 0, 1, 2가 아님!
funcs = [lambda i=i: i for i in range(3)]
[f() for f in funcs] # [0, 1, 2] ← 기본값으로 현재 i를 고정
람다는 i의 값이 아니라 변수 자체를 기억하기 때문에, 호출 시점의 i(마지막 값 2)를 읽는다.
map / filter는 결과를 바로 만들지 않는다. 이터레이터라서 list()로 감싸야 리스트가 되고, 한 번 순회하면 다시 쓸 수 없다.IllegalStateException이 난다.for문보다 느리다. 반복 횟수가 많은 핵심 로직에서는 for문을 쓰고, 스트림은 입력 파싱처럼 한 번만 실행되는 곳에 쓰자.int[] 스트림은 IntStream이다. mapToInt, boxed()로 Stream<Integer>와 오가야 할 때가 있다 #4 참고.reversed()는 타입 추론이 안 될 때가 있다. Comparator.comparing((String s) -> s.length()).reversed()처럼 매개변수 타입을 적어 준다.(a, b) -> 식lambda a, b: 식, 파이썬은 식 하나만Integer::parseIntint, lenkey=)Comparator.comparing이 파이썬 key=와 같은 방식map / filterfor문으로lambda i=i:로 값을 고정