[Java] Java 기초 - Collection

Hyunjun Kim·2025년 4월 8일
0

Data_Engineering

목록 보기
24/153

12. 콜렉션(Collection)

12.1 Collection 개념

12.1.1 콜렉션 프레임워크란

  • 다수의 데이터를 다루기 위한 자료구조를 표현하고 사용하는 클래스의 집합 이다.
  • 데이터를 다루는데 필요한 풍부하고 다양한 클래스와 기본함수를 제공하기 때문에 유용하다. 실제 자바 어플리케이션을 개발 할 때 가장 많이 사용하는 클래스.
  • 콜렉션 프레임워크의 모든 클래스는 Collection interface를 구현(implement)하는 클래스 또는 인터페이스다.

12.1.2 대표 콜렉션 인터페이스와 자료구조

Collection 은 모든 자료구조가 구현(implement)하는 인터페이스입니다. 아래 배우는 모든 자료구조에 해당하는 클래스, 인터페이스는 언제나 Collection 인터페이스를 구현하고 있다.

  1. List : 순서가 있는 데이터의 집합이며 데이터의 중복을 허용한다.
    → ArrayList, LinkedList, Stack 등
  2. Set : 순서를 유지하지 않는 데이터의 집합이며 데이터의 중복을 허용하지 않는다.
    → HashSet, TreeSet 등
  3. Map : 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합이다. 순서는 유지되지 않으며 키는 중복을 허용되지 않고 값은 중복을 허용한다.
    → HashMap, TreeMap 등
  4. Stack : 마지막에 넣은 데이터를 먼저 꺼내는 자료구조. LIFO(Last In First Out)
    → Stack, ArrayDeque 등
  5. Queue : 먼저 넣은 데이터를 먼저 꺼내는 자료구조. FIFO(First In First Out)
    → Queue, ArrayDeque 등
  • 콜렉션 인터페이스에는 콜렉션 클래스에 저장된 데이터를 읽고, 추가하고 삭제하는 등 데이터를 다루는데 기본적인 메소드들을 정의하고 있다.

12.2 List

순서가 있는 나열된 데이터를 표현한다.

  • ArrayList는 배열을 이용하여 데이터를 저장하는 클래스다. List interface를 구현한 클래스 중에 가장 많이 쓰이는 구현체.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(5);
        list.add(4);
        list.add(11);
        list.add(10);
        System.out.println(list);
        
        Collections.sort(list);
        System.out.println(list);
        System.out.println("size: " + list.size());
        
        list.remove(4);
        System.out.println(list);
        
        int[] intArr = new int[3];
        intArr[0] = 1;
        intArr[1] = 5;
        intArr[2] = 4;
        
        int[] intArr2 = new int[5];
        intArr2[0] = intArr[0];
        intArr2[1] = intArr[1];
        intArr2[2] = intArr[2];
        intArr2[3] = 11;
        intArr2[4] = 10;
        
        for (int i = 1; i <= list.size(); i++) {
            System.out.println(list.get(i));
        }
        for (int cur : list) {
            System.out.println(cur);
        }
    }
}

list라는 변수는 실제로 Interface 타입으로 선언했기 때문에 interface에 있는 함수만 쓸 수 있다. 실제 로직은 ArrayList에 있는 로직으로 돌리겠지만.

12.3 Set

Set은 순서를 유지하지 않는 데이터의 집합이며 데이터의 중복을 허용하지 않는다.

  • HashSet 은 Set 인터페이스를 구현한 대표적인 콜렉션이다. Set의 자료를 저장하고 조회할 때 HashTable을 이용해서 구현한다.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        Set<Integer> integerSet = new HashSet<>();
        integerSet.add(1);
        integerSet.add(3);
        integerSet.add(2);
        integerSet.add(9);
        integerSet.add(9);
        System.out.println(integerSet);
        
        Set<String> stringSet = new HashSet<>();
        stringSet.add("LA");
        stringSet.add("New York");
        stringSet.add("LasVegas");
        stringSet.add("San Francisco");
        stringSet.add("Seoul");
        System.out.println(stringSet);
        
        stringSet.remove("Seoul");
        System.out.println(stringSet);
        
        System.out.println("exist LA?" + stringSet.contains("LA"));
        
        List<String> removeTarget = new ArrayList<>();
        removeTarget.add("LA");
        removeTarget.add("New York");
        stringSet.removeAll(removeTarget);
        System.out.println(stringSet);
        
        System.out.println("exist LA?" + stringSet.contains("LA"));
        
        System.out.println("size: " + stringSet.size());
        stringSet.clear();
        System.out.println(stringSet);
    }
}

12.4 Map

Map은 키(key)와 값(value)을 하나의 데이터로 저장하는 특징을 가진다.

  • 같은 키를 가진 데이터는 하나만 존재할 수 있다.
    (같은 키로 put(write)를 여러번 하면 마지막 값만 남습니다.)
  • HashMap 은 Map 인터페이스를 구현한 대표적인 구현체다. HashTable로 구현되어있으며 데이터 검색에 O(1)의 시간복잡도로 조회할 수 있다.
import java.util.HashMap;
import java.util.Map;

public class MapExample {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "apple");
        map.put(2, "berry");
        map.put(3, "cherry");
        map.put(100, "pineapple");
        System.out.println(map);
        
        Map<String, String> stringMap = new HashMap<>();
        stringMap.put("first", "apple");
        stringMap.put("second", "berry");
        stringMap.putIfAbsent("second", "pineapple");
        stringMap.putIfAbsent("third", "pineapple");
        System.out.println(stringMap);
        
        if (stringMap.containsKey("first")) {
            System.out.println("exist!: " + stringMap.get("first"));
        }
        if (!stringMap.containsKey("noexist")) {
            System.out.println("doesn't exist!");
        }
        
        for (String key : stringMap.keySet()) {
            System.out.println(stringMap.get(key));
        }
        System.out.println(stringMap.values());
        
        stringMap.remove("first");
        System.out.println(stringMap);
        
        stringMap.clear();
        System.out.println("size: " + stringMap.size());
    }
}

선언부분에 <> : 이 부분은 제네릭표시다.

12.5 Stack

Stack 은 마지막에 저장한 데이터를 가장 먼저 꺼내는 자료구조다. 이것을 LIFO(Last In First Out) 라고 한다.

아래 그림을 보자. 먼저 삽입된 값인 17이 가장 아래로, 이후 삽입되는 값은 그 위에 쌓이기 시작한다. 이후, pop()을 통해 값을 반환할 때도 마지막에 삽입된 값인 45가 가장 먼저 반환된다.


이미지 출처 : https://coccagerman.medium.com/data-structures-stacks-7ec3c3247af1

12.5.1 스택의 예

웹브라우저의 앞페이지 이동 뒤페이지 이동 / 그릇 쌓기 등.

12.5.2 예제 코드

public class Main {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        stack.push(1);
        stack.push(3);
        stack.push(5);
        stack.push(7);
        System.out.println(stack); // Stack을 출력
        
        System.out.println(stack.peek()); // Stack의 가장 상단 값을 출력(삭제는 하지 않는다.)
        stack.pop(); // Stack의 가장 상단 값을 제거한다.
        System.out.println(stack);
        System.out.println(stack.size()); // Stack의 크기를 반환합.
        System.out.println(stack.contains(1)); // Stack에 1이라는 값이 있으면 true를, 그렇지 않으면 false를 반환
        System.out.println(stack.empty()); // STack이 비어있으면 true를, 그렇지 않으면 false를 반환
        System.out.println(stack);
    }
}

12.6 Queue

Queue 는 처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO(First In First Out) 구조로 되어있다.

아래 그림을 보자. 큐는 양 쪽 끝의 통로가 뚫려있다고 생각하면 된다. 가장 먼저 들어온 Data가 가장 먼저 반환된다.

를 활용하는 방식은 우선순위 큐, 원형 우선순위 큐, 원형 큐 등 다양하게 존재한다.

이미지 출처 : https://www.programiz.com/java-programming/queue

12.6.1 큐의 예

  • 은행 창구 줄서기 / 인쇄작업 대기목록

12.6.2 예제코드

public class QueueExample {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        queue.add(3);
        queue.add(5);
        System.out.println(queue);
        System.out.println("first element : " +  queue.peek());
        System.out.println(queue.size());
        System.out.println("first element : " + queue.poll());
        System.out.println(queue.size());
        System.out.println("first element : " +  queue.peek());

        queue.offer(2);
        queue.offer(4);
        queue.offer(8);
        System.out.println(queue);

        int lastSizeOfQueue = queue.size();
        for(int i=0; i< lastSizeOfQueue; i++ ){
            queue.poll();
        }
        System.out.println("is Empty : "+ queue.isEmpty());
    }
}
  • add()는 실패 시 예외 발생 → 예외 처리가 필요할 수 있음.
  • offer()는 실패 시 false 반환 → 실패를 if문으로 처리 가능.
  • 기능은 거의 같지만, 안정적인 코드를 위해서는 offer()가 더 많이 쓰임.

12.7 ArrayDeque

실무에서는 단순히 Stack, Queue 클래스 대신에 ArrayDeque을 많이 사용한다. 기본 Stack, Queue의 기능을 모두 포함하면서도 성능이 더 좋다.

  • 우리가 앞서 배운 큐는 한쪽에서만 값이 삽입되고 다른 한쪽에서만 값을 반환하는 자료구조였지만 deque의 경우 양 끝에서 삽입과 반환이 가능하다.


이미지 출처 : https://www.interviewbit.com/data-structure-interview-questions/


이미지 출처 : https://codegym.cc/groups/posts/java-deque

The name “Deque” means “Double Ended Queue”. “Deque” is pronounced like a "deck" of cards and you know what? It is somewhat similar to a real deck of cards: you may take a card from the bottom or the top of such a deck.


이미지 출처 : https://codegym.cc/groups/posts/java-deque

So you can enqueue and dequeue from both ends of a Java Deque, that means you can use a Deque as both queue and stack.

public class Main {
    public static void main(String[] args) {
        ArrayDeque<Integer> arrayDeque = new ArrayDeque<>(); // ArrayDeque를 이용한 선언(제네릭스 이용)
        arrayDeque.addFirst(1);
        arrayDeque.addFirst(2);
        arrayDeque.addFirst(3);
        arrayDeque.addFirst(4); // arrayDeque의 앞에 값을 삽입
        System.out.println(arrayDeque);
        
        arrayDeque.addLast(0); // arrayDeque의 끝에 값을 삽입
        System.out.println(arrayDeque);
        
        arrayDeque.offerFirst(10); // addFirst와 비슷하지만 큐의 크기 문제가 생길 때, offerFirst는 false를,
        // addFrist는 exception을 반환합니다.
        System.out.println(arrayDeque);
        
        arrayDeque.offerLast(-1); // arrayDeque의 끝에 값을 삽입
        System.out.println(arrayDeque);
        System.out.println(arrayDeque.size()); // 7
        
        
        System.out.println(arrayDeque.removeFirst()); // 첫번째 값을 제거하면서 그 값을 리턴
        System.out.println(arrayDeque.removeLast()); // 마지막 값을 제거하면서 그 값을 리턴
        System.out.println(arrayDeque);
        System.out.println(arrayDeque.size()); // 5
        System.out.println(arrayDeque.pollFirst()); // 첫번째 값을 반환 및 제거하면서 그 값을 리턴
        System.out.println(arrayDeque);
        System.out.println(arrayDeque.size()); // 4
        System.out.println(arrayDeque.pollLast()); // 마지막 값을 반환 및 제거하면서 그 값을 리턴
        System.out.println(arrayDeque);
        System.out.println(arrayDeque.size()); // 3
        System.out.println(arrayDeque.peekFirst()); // 첫번째 값을 반환, 제거하지 않음
        System.out.println(arrayDeque.peekLast()); // 마지막 값을 반환, 제거하지 않음
        System.out.println(arrayDeque.size()); // 3
    }
}
profile
Data Analytics Engineer 가 되

0개의 댓글