Java 코딩테스트 치트시트

NANO·4일 전

평소 Python으로 문제를 풀다가 Java로 코딩테스트를 봐야 할 때, 손에서 바로 나와야 하는 것들만 모았다. 알고리즘 이론보다 "Java로 생각을 코드로 옮기는 속도"에 초점을 맞췄다. (Java 17 기준)


1. 기본 템플릿과 입출력

오프라인 IDE 테스트는 채점기가 없으므로 main에서 직접 함수를 호출해 결과를 출력하는 형태가 기본이다. 입력이 주어지는 경우엔 BufferedReader가 안전하다.

import java.util.*;
import java.io.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        int[] arr = Arrays.stream(br.readLine().split(" "))
                          .mapToInt(Integer::parseInt).toArray();

        StringBuilder sb = new StringBuilder();
        for (int x : arr) sb.append(x).append('\n');
        System.out.print(sb);
    }
}
상황쓰는 것비고
입력 적음, 빨리 짜기Scanner sc = new Scanner(System.in); sc.nextInt(), sc.next(), sc.nextLine()nextInt() 뒤 nextLine()은 개행 하나 먹으니 한 번 더 호출
입력 10만 줄 이상BufferedReader + StringTokenizerreadLine()은 String 반환, 파싱 필요
출력 많음StringBuilder에 모아 마지막에 한 번 출력System.out.println 반복은 느림
한 줄 정수 여러 개Arrays.stream(line.split(" ")).mapToInt(Integer::parseInt).toArray()
문자열 → 숫자Integer.parseInt(s), Long.parseLong(s), Double.parseDouble(s)
숫자 → 문자열String.valueOf(n), Integer.toString(n), "" + n
진법 변환Integer.toBinaryString(n), Integer.toString(n, 2), Integer.parseInt("1011", 2)
포맷 출력String.format("%.2f", d), System.out.printf("%d %s%n", a, b)

과제형(플레이리스트 만들기 등)이면 입력 파싱 대신 main에 테스트 데이터를 직접 만들어 호출하고, 결과를 println으로 보여주면 된다. 클래스 여러 개는 한 파일에 class Song {}처럼 public 없이 두면 된다.


2. String / StringBuilder / char

String은 불변이라 반복 +=는 O(n²)다. 문자열을 만들거나 바꿀 땐 무조건 StringBuilder.

할 일코드
길이s.length() (배열은 arr.length, 리스트는 list.size())
i번째 문자s.charAt(i) → char
부분 문자열s.substring(from, to) (to 미포함), s.substring(from)
문자 배열로s.toCharArray() / 다시 문자열로 new String(chars)
분리s.split(" "), s.split(",") — 정규식이라 \\., `\
합치기String.join(",", list), String.join(" ", arr)
포함·위치s.contains("ab"), s.indexOf("ab") (없으면 -1), s.startsWith, s.endsWith
바꾸기s.replace("a", "b") (전부), s.replaceAll("[0-9]", "") (정규식)
대소문자s.toUpperCase(), s.toLowerCase()
공백 제거s.trim(), s.strip()
비교s.equals(t) (절대 == 금지), s.equalsIgnoreCase(t), s.compareTo(t) (사전순, 음수/0/양수)
비었는지s.isEmpty(), s.isBlank()
반복"ab".repeat(3)
문자 판별Character.isDigit(c), isLetter(c), isUpperCase(c), isAlphabetic(c)
문자 ↔ 숫자c - '0' (숫자 문자→int), (char)(n + '0'), c - 'a' (알파벳 인덱스)
문자 변환Character.toUpperCase(c), Character.toLowerCase(c)
StringBuilder sb = new StringBuilder();
sb.append("abc").append(1).append('x');
sb.insert(0, "z");        // 앞에 삽입
sb.deleteCharAt(sb.length() - 1);
sb.setCharAt(0, 'q');
sb.reverse();             // 뒤집기 — 팰린드롬 판별에 바로 쓰임
sb.setLength(0);          // 비우기
String result = sb.toString();

문자열 뒤집기는 new StringBuilder(s).reverse().toString(). 문자 정렬은 char[] c = s.toCharArray(); Arrays.sort(c); new String(c). 아스키 연산은 char가 정수로 자동 승격되니 (char)(c + 1)처럼 캐스팅 필요.


3. 배열과 Arrays 유틸

크기가 고정이면 배열, 늘어나면 ArrayList. int[]와 Integer[]는 다른 타입이라 Comparator 정렬은 Integer[]에만 된다.

int[] a = new int[n];                 // 0으로 초기화
int[] b = {3, 1, 2};
int[][] grid = new int[r][c];
boolean[][] visited = new boolean[r][c];
Arrays.fill(a, -1);                   // 전체 채우기
for (int[] row : grid) Arrays.fill(row, Integer.MAX_VALUE);  // 2차원은 행별로

Arrays.sort(a);                       // 오름차순 (기본형은 역순 직접 불가)
Integer[] boxed = {3, 1, 2};
Arrays.sort(boxed, Collections.reverseOrder());
Arrays.sort(a, from, to);             // 구간 정렬

int[] copy = Arrays.copyOf(a, a.length);
int[] part = Arrays.copyOfRange(a, 1, 4);   // [1,4)
int[] c2 = a.clone();

Arrays.toString(a);                   // 디버그 출력 "[1, 2, 3]"
Arrays.deepToString(grid);            // 2차원
Arrays.equals(a, b);
Arrays.stream(a).sum();  .max().getAsInt();  .min().getAsInt();
Arrays.asList(boxed);                 // 고정 크기 List (add 불가)
List<Integer> list = new ArrayList<>(Arrays.asList(boxed));   // 수정 가능
List<Integer> li = Arrays.stream(a).boxed().collect(Collectors.toList());   // int[] → List
int[] back = li.stream().mapToInt(Integer::intValue).toArray();            // List → int[]
int idx = Arrays.binarySearch(a, key);  // 정렬된 배열만, 없으면 음수

2차원 배열 정렬은 Arrays.sort(arr, (x, y) -> x[0] - y[0]) (int[][] 은 객체 배열이라 가능). 방향 배열은 int[] dr = {-1, 1, 0, 0}; int[] dc = {0, 0, -1, 1};로 두고 범위 검사 if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;를 반드시 먼저.


4. 컬렉션 핵심

과제형 문제의 8할은 ArrayList + HashMap으로 끝난다. 선언은 인터페이스 타입으로, 제네릭은 래퍼 타입(Integer, Long, Character)만 가능.

List

List<Integer> list = new ArrayList<>();
list.add(x);  list.add(0, x);  list.get(i);  list.set(i, x);
list.remove(i);                 // 인덱스로 삭제
list.remove(Integer.valueOf(x)); // 값으로 삭제 (int면 인덱스로 오해함)
list.size();  list.isEmpty();  list.contains(x);  list.indexOf(x);
Collections.sort(list);  list.sort(null);  list.sort(Comparator.reverseOrder());
Collections.reverse(list);  Collections.max(list);  Collections.min(list);
Collections.swap(list, i, j);
list.subList(from, to);         // 뷰(복사 아님)
new ArrayList<>(list);          // 복사
List.of(1, 2, 3);               // 불변

Map

Map<String, Integer> map = new HashMap<>();
map.put(k, v);
map.get(k);                     // 없으면 null → int에 넣으면 NPE
map.getOrDefault(k, 0);
map.put(k, map.getOrDefault(k, 0) + 1);   // 카운팅 기본
map.merge(k, 1, Integer::sum);            // 카운팅 한 줄
map.computeIfAbsent(k, x -> new ArrayList<>()).add(v);   // 그룹핑 한 줄
map.containsKey(k);  map.containsValue(v);  map.remove(k);  map.size();
for (Map.Entry<String, Integer> e : map.entrySet()) { e.getKey(); e.getValue(); }
for (String k : map.keySet()) {}
for (int v : map.values()) {}

TreeMap<Integer, String> tm = new TreeMap<>();   // 키 정렬 유지
tm.firstKey();  tm.lastKey();  tm.floorKey(x);  tm.ceilingKey(x);  tm.headMap(x);  tm.tailMap(x);
LinkedHashMap<>                                  // 삽입 순서 유지

Set

Set<Integer> set = new HashSet<>();
set.add(x);  set.contains(x);  set.remove(x);  set.size();
new HashSet<>(list).size();     // 중복 제거 개수
TreeSet<Integer> ts = new TreeSet<>();   // 정렬 유지
ts.first();  ts.last();  ts.floor(x);  ts.ceiling(x);  ts.pollFirst();

Deque (스택·큐 둘 다)

Deque<Integer> dq = new ArrayDeque<>();
// 큐: offer / poll / peek         (뒤에 넣고 앞에서 뺀다)
// 스택: push / pop / peek        (앞에 넣고 앞에서 뺀다)
dq.offerFirst(x);  dq.offerLast(x);  dq.pollFirst();  dq.pollLast();  dq.peekFirst();  dq.peekLast();
dq.isEmpty();
// Stack 클래스는 느리고 권장 안 함. ArrayDeque는 null 불가.

PriorityQueue

PriorityQueue<Integer> pq = new PriorityQueue<>();                          // 최소 힙
PriorityQueue<Integer> maxPq = new PriorityQueue<>(Collections.reverseOrder());
PriorityQueue<int[]> pq2 = new PriorityQueue<>((a, b) -> a[1] - b[1]);      // 배열 우선순위
pq.offer(x);  pq.poll();  pq.peek();  pq.size();  pq.isEmpty();
자료구조언제주요 연산 비용
ArrayList순서 있는 목록, 인덱스 접근get O(1), 중간 삽입/삭제 O(n)
HashMap / HashSet카운팅, 중복 체크, 키→값 조회O(1)
TreeMap / TreeSet정렬 상태 유지, 범위 검색O(log n)
LinkedHashMap삽입 순서 유지하는 Map (LRU 등)O(1)
ArrayDeque큐, 스택, BFSO(1)
PriorityQueue항상 최소/최대 꺼내기, 다익스트라O(log n)

5. 정렬과 Comparator

람다 (a, b) -> a - b는 오름차순, b - a는 내림차순. 값이 클 수 있으면 뺄셈 대신 Integer.compare(a, b)를 써야 오버플로우를 피한다.

// 기본
Collections.sort(list);
list.sort(Comparator.reverseOrder());
Arrays.sort(arr2d, (a, b) -> a[0] - b[0]);

// 다중 조건: 점수 내림차순, 같으면 이름 오름차순
list.sort((a, b) -> {
    if (a.score != b.score) return Integer.compare(b.score, a.score);
    return a.name.compareTo(b.name);
});

// 같은 것을 Comparator 체이닝으로 (면접에서 더 좋아 보임)
list.sort(Comparator.comparingInt((Song s) -> s.score).reversed()
                    .thenComparing(s -> s.name));

// 문자열 길이 순, 같으면 사전순
words.sort(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()));

// Map을 값 기준으로 정렬해 상위 N개
List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
entries.sort((a, b) -> b.getValue() - a.getValue());

객체가 기본 정렬 기준을 가지게 하려면 Comparable 구현:

class Song implements Comparable<Song> {
    String title; int plays;
    Song(String title, int plays) { this.title = title; this.plays = plays; }
    @Override
    public int compareTo(Song o) { return Integer.compare(o.plays, this.plays); }  // 재생수 내림차순
    @Override
    public String toString() { return title + "(" + plays + ")"; }
}

Collections.sort와 Arrays.sort(객체)는 안정 정렬(TimSort)이라 같은 키는 원래 순서 유지. Arrays.sort(int[])는 듀얼 피벗 퀵소트로 최악 O(n²)가 있으나 실무 테스트에선 신경 안 써도 됨.


6. Stream 핵심 패턴

과제형에서 "필터링해서 정렬해서 상위 N개" 같은 요구는 Stream 한 줄이 가장 읽기 좋다. 다만 성능 질문이 나오면 "반복문과 동일한 O(n), 가독성 때문에 썼다"고 답하면 된다.

import java.util.stream.*;

// 필터 + 변환 + 수집
List<String> titles = songs.stream()
    .filter(s -> s.plays > 100)
    .map(s -> s.title)
    .collect(Collectors.toList());      // Java 16+ 면 .toList()

// 정렬 + 상위 N
List<Song> top3 = songs.stream()
    .sorted(Comparator.comparingInt((Song s) -> s.plays).reversed())
    .limit(3)
    .collect(Collectors.toList());

// 그룹핑: 아티스트별 곡 목록 / 개수 / 합계
Map<String, List<Song>> byArtist = songs.stream().collect(Collectors.groupingBy(s -> s.artist));
Map<String, Long> countByArtist = songs.stream().collect(Collectors.groupingBy(s -> s.artist, Collectors.counting()));
Map<String, Integer> playsByArtist = songs.stream().collect(Collectors.groupingBy(s -> s.artist, Collectors.summingInt(s -> s.plays)));

// 집계
int total = songs.stream().mapToInt(s -> s.plays).sum();
Optional<Song> best = songs.stream().max(Comparator.comparingInt(s -> s.plays));
double avg = songs.stream().mapToInt(s -> s.plays).average().orElse(0);
boolean any = songs.stream().anyMatch(s -> s.plays == 0);   // allMatch, noneMatch
long cnt = songs.stream().filter(s -> s.plays > 0).count();

// 중복 제거, 문자열 합치기
List<String> artists = songs.stream().map(s -> s.artist).distinct().collect(Collectors.toList());
String joined = titles.stream().collect(Collectors.joining(", "));

// 범위 반복, 문자열 문자 순회
IntStream.range(0, n).forEach(i -> ...);          // 0..n-1
IntStream.rangeClosed(1, n).sum();
s.chars().filter(Character::isDigit).count();

// List → Map (키 중복 시 터짐, 세 번째 인자로 병합 규칙)
Map<String, Song> byTitle = songs.stream().collect(Collectors.toMap(s -> s.title, s -> s, (a, b) -> a));

Optional은 .orElse(기본값), .orElseThrow(), .isPresent(), .get()로 꺼낸다. Collectors.toList()로 만든 리스트는 수정 가능, .toList()(Java 16+)는 불변이라 add 하면 예외.


7. 자주 나오는 알고리즘 패턴

1시간 오프라인 테스트에서 실제로 나오는 건 이 여섯 개가 거의 전부다. 각각 손에서 바로 나와야 한다.

완전탐색 (순열·조합·부분집합)

// 조합: n개 중 r개 고르기
static void comb(int[] arr, int start, int r, List<Integer> cur, List<List<Integer>> out) {
    if (cur.size() == r) { out.add(new ArrayList<>(cur)); return; }
    for (int i = start; i < arr.length; i++) {
        cur.add(arr[i]);
        comb(arr, i + 1, r, cur, out);
        cur.remove(cur.size() - 1);
    }
}
// 순열은 start 대신 boolean[] used 로, 부분집합은 비트마스크 for (int mask = 0; mask < (1 << n); mask++)

투포인터 / 슬라이딩 윈도우

// 합이 target 이상인 최소 길이 구간
int left = 0, sum = 0, best = Integer.MAX_VALUE;
for (int right = 0; right < n; right++) {
    sum += arr[right];
    while (sum >= target) {
        best = Math.min(best, right - left + 1);
        sum -= arr[left++];
    }
}

누적합

int[] pre = new int[n + 1];
for (int i = 0; i < n; i++) pre[i + 1] = pre[i] + arr[i];
// [l, r] 구간 합 = pre[r + 1] - pre[l]

BFS (그리드 최단거리)

int[] dr = {-1, 1, 0, 0}, dc = {0, 0, -1, 1};
int[][] dist = new int[R][C];
for (int[] row : dist) Arrays.fill(row, -1);
Deque<int[]> q = new ArrayDeque<>();
q.offer(new int[]{sr, sc}); dist[sr][sc] = 0;
while (!q.isEmpty()) {
    int[] cur = q.poll();
    for (int d = 0; d < 4; d++) {
        int nr = cur[0] + dr[d], nc = cur[1] + dc[d];
        if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
        if (grid[nr][nc] == 1 || dist[nr][nc] != -1) continue;
        dist[nr][nc] = dist[cur[0]][cur[1]] + 1;
        q.offer(new int[]{nr, nc});
    }
}

DFS (인접 리스트, 연결 요소)

List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i < n; i++) adj.add(new ArrayList<>());
adj.get(a).add(b); adj.get(b).add(a);

boolean[] visited = new boolean[n];
static void dfs(int u) {
    visited[u] = true;
    for (int v : adj.get(u)) if (!visited[v]) dfs(v);
}
// 재귀 깊이 1만 넘으면 스택으로 바꿀 것 (Java 기본 스택은 작음)

이분탐색

// 정렬된 배열에서 target 이상인 첫 위치 (lower bound)
int lo = 0, hi = n;
while (lo < hi) {
    int mid = (lo + hi) >>> 1;
    if (arr[mid] < target) lo = mid + 1; else hi = mid;
}
// 매개변수 탐색: "X가 가능한가"가 단조이면 같은 틀로 최대/최소 X를 찾는다

유니온 파인드 (그룹 묶기)

int[] parent;
static int find(int x) { return parent[x] == x ? x : (parent[x] = find(parent[x])); }
static void union(int a, int b) { parent[find(a)] = find(b); }

유틸 함수

Math.max(a, b);  Math.min(a, b);  Math.abs(x);  Math.pow(2, 10);  Math.sqrt(x);  Math.floorMod(a, m);
long gcd(long a, long b) { return b == 0 ? a : gcd(b, a % b); }   // lcm = a / gcd * b
Integer.MAX_VALUE;  Integer.MIN_VALUE;  Long.MAX_VALUE;
Integer.bitCount(n);  1 << k;  (mask & (1 << k)) != 0;

8. Python 유저가 Java에서 자주 하는 실수

이 표의 절반은 컴파일 에러로 바로 잡히지만, 나머지 절반은 조용히 틀린 답을 낸다. 특히 ==, 정수 나눗셈, 오버플로우는 테스트 끝나기 전에 한 번씩 훑어볼 것.

함정틀린 예맞는 예
문자열 비교s == "abc"s.equals("abc")
Integer 비교 (128 이상이면 틀림)list.get(i) == list.get(j)list.get(i).equals(list.get(j)) 또는 intValue()
정수 나눗셈7 / 2 → 3 (Python 3.5 기대)7 / 2.0 또는 (double) a / b
음수 나머지-7 % 3 → -1Math.floorMod(-7, 3) → 2
int 오버플로우 (약 21억)int sum = a * blong sum = (long) a * b — 곱하기 전에 캐스팅
오버플로우 Comparator(a, b) -> a - b (큰 값)Integer.compare(a, b)
char + 숫자'a' + 1 → 98 (int)(char)('a' + 1) → 'b'
배열 출력System.out.println(arr) → 주소Arrays.toString(arr)
기본형 배열 역순 정렬Arrays.sort(intArr, reverseOrder()) 컴파일 에러Integer[]로 바꾸거나 정렬 후 뒤집기
List.remove 오해list.remove(3) → 인덱스 3 삭제값 삭제는 list.remove(Integer.valueOf(3))
순회 중 삭제for-each 안에서 list.remove() → ConcurrentModificationExceptionremoveIf(x -> ...) 또는 Iterator
Map.get nullint v = map.get(k) → NPEmap.getOrDefault(k, 0)
제네릭에 기본형List<int>List<Integer>
스트림 재사용같은 stream 두 번 소비매번 .stream() 새로
스택 오버플로우재귀 깊이 수만반복 + ArrayDeque로 전환
객체 복사List<Integer> b = a (같은 참조)new ArrayList<>(a)
리스트 안 리스트 복사out.add(cur) (백트래킹에서 다 같은 객체)out.add(new ArrayList<>(cur))
substring 범위s.substring(i, j) j 포함으로 착각j 미포함 (Python 슬라이스와 동일)
리스트 마지막list.get(-1)list.get(list.size() - 1)
자료구조 다시 보기적은 배열에서만 테스트빈 입력, 한 개 입력, 중복, 최대 크기 확인

문법 상 자주 막히는 것: 삼항은 a ? b : c, 향상된 for문은 for (int x : arr), static 메서드에서는 인스턴스 필드 접근 불가(헬퍼 메서드를 모두 static으로 통일하거나 객체를 만들어 호출), 예외를 던지는 메서드 쓰면 throws IOException 또는 try-catch. 프로그래머스 스타일은 class Solution { public int solution(int[] arr) {...} }이니 그 형태도 익숙해지기.


9. IntelliJ 필수 단축키와 낯선 환경 대비

회사 장비는 맥북일 수도 윈도우일 수도 있다. 이 열 개만 양쪽으로 외우면 된다. 마우스로 해도 되니 기억 안 나면 그냥 클릭.

할 일MacWindows
실행Ctrl + RShift + F10
현재 파일 main 실행Ctrl + Shift + RCtrl + Shift + F10
자동완성Ctrl + SpaceCtrl + Space
import 자동 추가Opt + Enter (빨간 줄에서)Alt + Enter
모든 import 정리Ctrl + Opt + OCtrl + Alt + O
코드 포맷Cmd + Opt + LCtrl + Alt + L
줄 복제Cmd + DCtrl + D
줄 삭제Cmd + BackspaceCtrl + Y
줄 이동Opt + Shift + ↑/↓Alt + Shift + ↑/↓
주석 토글Cmd + /Ctrl + /
이름 일괄 변경Shift + F6Shift + F6
메서드 추출Cmd + Opt + MCtrl + Alt + M
정의로 이동Cmd + BCtrl + B
파라미터 힌트Cmd + PCtrl + P
어디서나 검색Shift ShiftShift Shift
에러 위치로F2F2

라이브 템플릿 (타이핑 후 Tab): main → main 메서드, sout → println, fori → for (int i = 0; ...), iter → 향상된 for, psvm → main, ifn → if null, .var (식 뒤에) → 변수 추출, .for → 반복문 감싸기.

시작 직후 할 일 (2분)

  1. 새 프로젝트가 아니라 이미 열려 있는지, Java 버전이 몇인지 확인 (.toList(), var, record는 16+ / 10+ / 16+)
  2. Main 클래스에 sout("hi") 하고 실행해서 콘솔 나오는지 확인
  3. 한글 입력 전환이 어떻게 되는지 확인 (윈도우면 한/영 키, 맥이면 Caps Lock 또는 Ctrl+Space)
  4. 자동완성이 안 뜨면 Ctrl + Space 수동 호출, 그래도 안 되면 외운 대로 친다

테스트 중 전략: 먼저 클래스와 메서드 시그니처만 빈 몸으로 다 만들어 놓고 설명 주석 한 줄씩 달기 → 핵심 로직 구현 → main에서 테스트 데이터로 호출해 출력 → 엣지 케이스 추가. 시간이 끝나도 뼈대가 다 있으면 면접에서 설명할 게 남는다.


10. 면접에서 "왜 이 자료구조를 썼나" 한 줄 답변

코드를 놓고 바로 면접이 이어지므로, 선택마다 이유를 한 문장으로 말할 수 있어야 한다. 답의 형식은 항상 "필요한 연산 + 그 연산에서 이 구조가 빠른 이유 + 대안을 안 고른 이유".

질문답
왜 HashMap?키로 값을 찾는 게 핵심이고 평균 O(1)이라. 순서나 정렬이 필요했다면 TreeMap이나 LinkedHashMap을 썼을 겁니다.
왜 HashSet?중복 제거와 포함 여부 확인만 필요하고 값은 필요 없어서. List.contains는 O(n)이라 안 썼습니다.
왜 ArrayList?인덱스 접근과 마지막에 추가가 대부분이라. 중간 삽입/삭제가 많았다면 LinkedList나 다른 구조를 고려했을 겁니다.
왜 ArrayDeque? Stack 클래스는?Stack은 Vector 기반이라 동기화 오버헤드가 있고 공식적으로도 Deque 사용을 권장합니다.
왜 PriorityQueue?매번 최소/최대만 꺼내면 되니 전체 정렬 O(n log n) 대신 삽입·삭제 O(log n)으로 충분해서.
왜 TreeMap?키 정렬 상태를 유지하면서 범위 검색(floor/ceiling)이 필요해서. 조회만 하면 HashMap이 더 빠릅니다.
왜 StringBuilder?String은 불변이라 반복 결합이 O(n²)가 되어서.
왜 Stream? / 왜 반복문?Stream은 의도가 읽히고 복잡도는 동일. 다만 디버깅이나 이른 종료(break)가 필요한 곳은 반복문을 썼습니다.
시간 복잡도는?입력 n에 대해 정렬 O(n log n)이 지배적이고 나머지는 O(n)입니다. (자기 코드의 가장 비싼 연산 하나를 집어 말하기)
데이터가 100만 개면?메모리는 객체당 약 바이트 계산해서 말하고, 병목 연산을 짚고, 인덱스나 페이징으로 풀겠다고.
동시에 수정하면?HashMap은 스레드 안전하지 않으니 ConcurrentHashMap이나 동기화, 실제 서비스면 DB 트랜잭션으로.
예외 처리는?입력 검증은 경계에서 한 번, 내부는 신뢰. null은 Optional이나 기본값으로.
테스트는 어떻게?빈 입력, 한 개, 중복, 경계값을 main에서 확인했고, 실무면 JUnit으로 분리합니다.
다 못 풀었는데?여기까지 구현했고, 남은 부분은 이런 방법으로 할 계획이었다고 구체적으로. 변명 말고 설계를 말하기.

하나 더: 변수명은 a, tmp 대신 playCount, songsByArtist처럼 의미 있게. 코드 스타일을 본다고 했으니 이게 점수에 직접 들어간다.

profile
즐거운 토마토

0개의 댓글