[2026] Java 컬렉션 | ArrayList, HashMap, Set

[2026] Java 컬렉션 | ArrayList, HashMap, Set

이 글의 핵심

Java 컬렉션: ArrayList, HashMap, Set. List 인터페이스·Set 인터페이스.

들어가며

컬렉션 프레임워크는 List·Set·Map 등으로 데이터를 담는 공통 설계도를 제공합니다. 같은 인터페이스 아래에 구현체만 바꿔 성능·순서 요구에 맞출 수 있습니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. List 인터페이스

ArrayList

Java에서 가장 많이 사용하는 동적 배열 구조입니다: 다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
    public static void main(String[] args) {
        // ArrayList<String>: String 타입만 저장하는 리스트
        // List<String>: 인터페이스 타입으로 선언 (다형성)
        // new ArrayList<>(): 다이아몬드 연산자 (타입 추론)
        List<String> fruits = new ArrayList<>();
        
        // add: 리스트 끝에 요소 추가 - O(1) 분할상환
        fruits.add("사과");
        fruits.add("바나나");
        fruits.add("오렌지");
        
        // add(index, element): 특정 위치에 삽입 - O(n)
        // 인덱스 1 이후의 모든 요소를 오른쪽으로 이동
        fruits.add(1, "딸기");  // [사과, 딸기, 바나나, 오렌지]
        
        // get: 인덱스로 요소 접근 - O(1)
        // 내부적으로 배열이므로 즉시 접근 가능
        System.out.println(fruits.get(0));  // 사과
        System.out.println("크기: " + fruits.size());  // 4
        
        // set: 특정 인덱스의 값 변경 - O(1)
        fruits.set(0, "청사과");  // [청사과, 딸기, 바나나, 오렌지]
        
        // remove(index): 인덱스로 삭제 - O(n)
        // 삭제 후 뒤의 요소들을 왼쪽으로 이동
        fruits.remove(0);  // [딸기, 바나나, 오렌지]
        
        // remove(Object): 값으로 삭제 - O(n)
        // 순회하며 equals()로 비교하여 찾음
        fruits.remove("바나나");  // [딸기, 오렌지]
        
        // 순회: enhanced for loop (for-each)
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        
        // contains: 요소 존재 여부 확인 - O(n)
        // 순회하며 equals()로 비교
        if (fruits.contains("오렌지")) {
            System.out.println("오렌지 있음");
        }
        
        // clear: 모든 요소 제거 - O(n)
        fruits.clear();
        
        // isEmpty: 비어있는지 확인 - O(1)
        System.out.println("비어있음: " + fruits.isEmpty());  // true
    }
}

ArrayList의 내부 구조:

  • 내부적으로 배열(Object[])을 사용
  • 초기 용량: 10 (기본값)
  • 용량 부족 시: 1.5배로 확장 (재할당 + 복사)
  • 인덱스 접근: O(1), 삽입/삭제: O(n) 시간 복잡도 정리: | 연산 | 시간 복잡도 | 설명 | |-----|----------|------| | get(index) | O(1) | 배열 인덱스 접근 | | add(element) | O(1) 분할상환 | 끝에 추가 | | add(index, element) | O(n) | 중간 삽입 (요소 이동) | | remove(index) | O(n) | 삭제 후 요소 이동 | | contains(element) | O(n) | 순회하며 검색 |

LinkedList

다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.LinkedList;
LinkedList<Integer> numbers = new LinkedList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 앞/뒤 추가
numbers.addFirst(0);
numbers.addLast(4);
// 앞/뒤 제거
numbers.removeFirst();
numbers.removeLast();
System.out.println(numbers);  // [1, 2, 3]

2. Set 인터페이스

HashSet

중복을 허용하지 않는 집합(Set) 자료구조입니다: 다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
    public static void main(String[] args) {
        // HashSet: 해시 테이블 기반 집합
        // 중복 불가, 순서 보장 안 됨
        Set<Integer> numbers = new HashSet<>();
        
        // add: 요소 추가 - O(1) 평균
        // 내부적으로 hashCode()를 계산하여 저장 위치 결정
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(1);  // 중복 무시 (add()가 false 반환)
        
        // HashSet은 순서를 보장하지 않음
        // 출력 순서는 실행마다 다를 수 있음
        System.out.println(numbers);  // [1, 2, 3] (순서 무작위)
        
        // contains: 요소 존재 여부 확인 - O(1) 평균
        // hashCode()로 위치를 찾아서 equals()로 비교
        if (numbers.contains(2)) {
            System.out.println("2 있음");
        }
        
        // remove: 요소 삭제 - O(1) 평균
        numbers.remove(2);
        
        // 집합 연산 (수학의 집합 개념)
        Set<Integer> set1 = new HashSet<>();
        set1.add(1);
        set1.add(2);
        set1.add(3);  // set1 = {1, 2, 3}
        
        Set<Integer> set2 = new HashSet<>();
        set2.add(2);
        set2.add(3);
        set2.add(4);  // set2 = {2, 3, 4}
        
        // 합집합 (Union): set1 ∪ set2
        // 두 집합의 모든 요소 (중복 제거)
        Set<Integer> union = new HashSet<>(set1);  // set1 복사
        union.addAll(set2);  // set2의 모든 요소 추가
        System.out.println("합집합: " + union);  // [1, 2, 3, 4]
        
        // 교집합 (Intersection): set1 ∩ set2
        // 두 집합에 모두 있는 요소만
        Set<Integer> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);  // set2에 있는 것만 유지
        System.out.println("교집합: " + intersection);  // [2, 3]
        
        // 차집합 (Difference): set1 - set2
        // set1에는 있지만 set2에는 없는 요소
        Set<Integer> difference = new HashSet<>(set1);
        difference.removeAll(set2);  // set2에 있는 것 제거
        System.out.println("차집합: " + difference);  // [1]
    }
}

HashSet의 중복 제거 원리:

  1. add() 호출 시 요소의 hashCode() 계산
  2. 해시 코드로 저장 위치(버킷) 결정
  3. 같은 버킷에 이미 요소가 있으면 equals()로 비교
  4. 같은 요소면 추가하지 않음 (중복 제거) 시간 복잡도:
  • add(), remove(), contains(): O(1) 평균, O(n) 최악
  • 순서가 필요하면 LinkedHashSet (삽입 순서 유지)
  • 정렬이 필요하면 TreeSet (자동 정렬, O(log n))

TreeSet (정렬된 Set)

아래 코드는 java를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.TreeSet;
TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
System.out.println(numbers);  // [1, 2, 5, 8] (자동 정렬)
System.out.println("최소: " + numbers.first());
System.out.println("최대: " + numbers.last());

3. Map 인터페이스

HashMap

다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> ages = new HashMap<>();
        
        // 추가
        ages.put("홍길동", 25);
        ages.put("김철수", 30);
        ages.put("이영희", 28);
        
        // 조회
        System.out.println(ages.get("홍길동"));  // 25
        
        // 기본값
        System.out.println(ages.getOrDefault("박민수", 0));  // 0
        
        // 존재 여부
        if (ages.containsKey("홍길동")) {
            System.out.println("홍길동 있음");
        }
        
        // 수정
        ages.put("홍길동", 26);  // 덮어쓰기
        
        // 없을 때만 추가
        ages.putIfAbsent("홍길동", 27);  // 무시됨
        ages.putIfAbsent("박민수", 35);  // 추가됨
        
        // 삭제
        ages.remove("김철수");
        
        // 순회
        for (String key : ages.keySet()) {
            System.out.println(key + ": " + ages.get(key));
        }
        
        // 키-값 쌍 순회
        for (Map.Entry<String, Integer> entry : ages.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

TreeMap (정렬된 Map)

아래 코드는 java를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.

import java.util.TreeMap;
TreeMap<String, Integer> map = new TreeMap<>();
map.put("Charlie", 30);
map.put("Alice", 25);
map.put("Bob", 28);
System.out.println(map);  // {Alice=25, Bob=28, Charlie=30} (키로 정렬)

4. 컬렉션 유틸리티

Collections 클래스

다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.*;
public class CollectionsExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(Arrays.asList(3, 1, 4, 1, 5, 9));
        
        // 정렬
        Collections.sort(numbers);
        System.out.println(numbers);  // [1, 1, 3, 4, 5, 9]
        
        // 역순 정렬
        Collections.sort(numbers, Collections.reverseOrder());
        System.out.println(numbers);  // [9, 5, 4, 3, 1, 1]
        
        // 섞기
        Collections.shuffle(numbers);
        
        // 최대/최소
        System.out.println("최대: " + Collections.max(numbers));
        System.out.println("최소: " + Collections.min(numbers));
        
        // 빈도
        System.out.println("1의 개수: " + Collections.frequency(numbers, 1));
    }
}

5. 실전 예제

예제: 학생 성적 관리

다음은 java를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import java.util.*;
class Student {
    String name;
    int score;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}
public class GradeManager {
    private Map<String, Student> students;
    
    public GradeManager() {
        students = new HashMap<>();
    }
    
    public void addStudent(String id, String name, int score) {
        students.put(id, new Student(name, score));
    }
    
    public Student getStudent(String id) {
        return students.get(id);
    }
    
    public double getAverage() {
        if (students.isEmpty()) return 0;
        
        int sum = 0;
        for (Student student : students.values()) {
            sum += student.score;
        }
        return (double) sum / students.size();
    }
    
    public List<Student> getTopStudents(int n) {
        List<Student> list = new ArrayList<>(students.values());
        list.sort((a, b) -> b.score - a.score);
        return list.subList(0, Math.min(n, list.size()));
    }
    
    public static void main(String[] args) {
        GradeManager manager = new GradeManager();
        manager.addStudent("001", "홍길동", 95);
        manager.addStudent("002", "김철수", 88);
        manager.addStudent("003", "이영희", 92);
        
        System.out.println("평균: " + manager.getAverage());
        
        System.out.println("\n상위 2명:");
        for (Student s : manager.getTopStudents(2)) {
            System.out.println(s.name + ": " + s.score);
        }
    }
}

정리

핵심 요약

  1. List: ArrayList(빠른 조회), LinkedList(빠른 삽입/삭제)
  2. Set: HashSet(순서 없음), TreeSet(정렬됨)
  3. Map: HashMap(순서 없음), TreeMap(정렬됨)
  4. 제네릭: <T>로 타입 안전성
  5. Collections: 유틸리티 메서드

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3