ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] #4 배열(Array), 컬렉션(Collection), 맵(Map)
    Software engineer/Java 2023. 4. 30. 15:55

    개발을 하다 보면 많은 양의 데이터를 다뤄야 한다. 그럴 경우 굉장히 유용한 방법은 배열과 컬렉션 그리고 맵을 사용하는 방법이다.
     

    배열(Array)

    배열이란 기본형과 참조형 타입의 데이터를 순서대로 저장하고 처리하는 자료구조입니다.
    만약 10명의 나이를 저장하고 처리해야한다고 했을 때 10개의 변수를 선언하고 그들의 평균을 구한다고 하면 10개 변수를 모두 더하고 나누는 귀찮은 작업을 해야 한다. 배열을 이용하면 손쉽게 처리가 가능하다.

    int totalAge = 0;
    int[] ageArray = new int[]{12, 23, 31, 21, 22, 28, 19, 18, 22, 41 };
    for(int age:ageArray){
    	totalAge+=age;
    }
    System.out.println("avgAge=" + (totalAge/ageArray.length));

     
    배열은 또한 순서가 있는 데이터의 집합이다.

    배열과  index

    위의 그림처럼 배열은 순서대로 번호를 가지고 있다. 이를  index라고 하며 0부터 시작한다. 예를들어 총길이가 9라고 할 때 0~8까지의 index를 가진다. 또한 배열에 경우 한번 선언된 배열길이를 수정할 수 없다. 만약 배열의 길이를 수정해야 한다면 새로운 길이의 배열을 선언 후에 기존에 있던 배열을 복사하는 방식으로 구현해야 할 것이다.

    int[] ageArray = new int[]{12, 23, 31, 21, 22, 28, 19, 18, 22, 41 };
    System.out.println("ageArray[1]=" + ageArray[1]); 
    
    // result:
    // ageArray[1]=23

     
    배열은 기본적으로 Java에서 제공하는 Arrays클래스의 기능들을 사용하면 더 편리하게 배열의 데이터들을 처리하는 게 가능하다.
    Arrays link
     
     

    컬렉션(Collection) 그리고 맵(Map)

    Java Collection Framework는 Java에서 제공하는 데이터 구조를 관리하는 클래스의 집합입니다. 이러한 클래스는 데이터를 저장하고 검색하고 조작하는 데 사용됩니다. Java Collection Framework는 데이터를 다루기 위해 성능과 유연성을 갖춘 많은 인터페이스와 클래스를 제공합니다.

    Java Collection Framework의 핵심 인터페이스는 Collection, Set, List, Queue 및 Map입니다. Collection 인터페이스는 요소의 모음을 나타내며, Set은 중복 요소를 허용하지 않는 컬렉션, List는 순서가 있는 요소의 모음, Queue는 요소의 순서를 유지하면서 요소를 추가하고 제거할 수 있는 컬렉션, Map은 키와 값으로 구성된 쌍의 집합을 나타냅니다.
     

    List

    Java Collection Framework에서 List 인터페이스는 순서가 있는 요소의 모음을 나타내는 인터페이스입니다. 이 인터페이스를 구현하는 클래스는 순서가 있는 요소들을 저장하고 관리할 수 있습니다. 이번에는 List 인터페이스를 구현한 클래스들과 각각의 예제를 살펴보겠습니다.

    ArrayList
    ArrayList 클래스는 크기가 가변적인 배열을 나타냅니다. ArrayList 클래스는 배열과 비슷하지만, 크기가 동적으로 조정될 수 있습니다.

    import java.util.ArrayList;
    import java.util.List;
    
    public class ArrayListExample {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("apple");
            list.add("banana");
            list.add("orange");
            System.out.println(list.get(0));
            System.out.println(list.size());
            list.remove(1);
            System.out.println(list);
        }
    }
    

    위 예제에서는 ArrayList 클래스를 사용하여 List를 구현하고 있습니다. List에 세 가지 과일을 추가하고, get() 메서드를 사용하여 첫 번째 과일 "apple"을 출력합니다. 또한, size() 메서드를 사용하여 List의 크기를 출력하고, remove() 메서드를 사용하여 두 번째 과일 "banana"을 삭제합니다. 마지막으로, List의 모든 요소를 출력합니다.

    LinkedList
    LinkedList 클래스는 이중 연결 리스트를 나타냅니다. 이중 연결 리스트란 각각의 저장공간에 데이터와 다음번 데이터의 주소값을 저장하여 연결하는 자료구조입니다.

    이러한 구조 덕분에 LinkedList 클래스는 ArrayList와는 다르게 중간에 요소를 삽입하거나 삭제할 때 좋은 성능을 보입니다. 하지만 검색에 있어서는 느리다는 단점이 있습니다.

    import java.util.LinkedList;
    import java.util.List;
    
    public class LinkedListExample {
        public static void main(String[] args) {
            List<String> list = new LinkedList<String>();
            list.add("apple");
            list.add("banana");
            list.add("orange");
            System.out.println(list.get(1));
            System.out.println(list.size());
            list.remove(2);
            System.out.println(list);
        }
    }
    

    위 예제에서는 LinkedList 클래스를 사용하여 List를 구현하고 있습니다. List에 세 가지 과일을 추가하고, get() 메서드를 사용하여 두 번째 과일 "banana"을 출력합니다. 또한, size() 메서드를 사용하여 List의 크기를 출력하고, remove() 메서드를 사용하여 세 번째 과일 "orange"을 삭제합니다. 마지막으로, List의 모든 요소를 출력합니다.

    Vector
    Vector 클래스는 ArrayList와 매우 비슷하지만, Vector 클래스는 동기화된 메서드로 구성되어 있어 멀티스레드 환경에서 안전하게 사용할 수 있습니다. Vector 클래스는 Java 1.0 버전부터 존재하며, Java Collection Framework가 도입되기 전부터 사용되어 왔습니다.

    import java.util.Vector;
    
    public class VectorThreadExample {
        private static Vector<Integer> vector = new Vector<Integer>();
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Thread thread = new Thread(new Runnable() {
                    public void run() {
                        for (int j = 0; j < 1000; j++) {
                            vector.add(j);
                        }
                    }
                });
                thread.start();
            }
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(vector.size());
        }
    }
    

    위 예제에서는 10개의 스레드가 Vector에 1000개의 요소를 추가하도록 구성되어 있습니다. 모든 스레드가 동시에 Vector에 액세스 할 수 있기 때문에, Vector 클래스를 사용하지 않는 경우 멀티스레드 환경에서 문제가 발생할 수 있습니다.
    하지만, Vector 클래스를 사용하면 스레드 간에 안전하게 요소를 추가하고 제거할 수 있습니다. Vector 클래스는 동기화된 메서드를 사용하여 요소의 추가, 제거, 액세스를 안전하게 수행합니다. 따라서, 위 예제에서 Vector 클래스를 사용하면 모든 스레드가 안전하게 요소를 추가할 수 있습니다.
    위 예제에서는 Vector 클래스를 사용하여 요소를 추가하고 있으므로, 모든 스레드가 안전하게 요소를 추가할 수 있습니다. 스레드가 Vector에 접근할 때 다른 스레드가 동시에 접근하지 않도록 동기화되어 있기 때문입니다. 마지막으로, Vector의 크기를 출력하여 모든 스레드가 추가한 요소의 총개수를 확인할 수 있습니다.

    Stack

    스택은 LIFO(Last-In-First-Out)의 데이터 구조로, 위의 그림처럼 마지막에 추가된 데이터가 가장 먼저 제거됩니다. Vector 클래스를 상속받은 Stack 클래스는 스택 자료구조를 구현하는 클래스입니다.
    Stack 클래스는 Vector 클래스를 상속받기 때문에, Vector 클래스에서 제공하는 모든 메서드를 상속받습니다. 그리고 Stack 클래스는 스택 자료구조에서 사용되는 메서드인 push(), pop(), peek() 메서드를 추가로 제공합니다.

    • push(element) : 스택의 맨 위에 요소를 추가합니다.
    • pop() : 스택의 맨 위에 있는 요소를 제거하고 반환합니다.
    • peek() : 스택의 맨 위에 있는 요소를 반환합니다. 제거하지는 않습니다.
    import java.util.Stack;
    
    public class StackExample {
        public static void main(String[] args) {
            Stack<String> stack = new Stack<String>();
            stack.push("apple");
            stack.push("banana");
            stack.push("orange");
            System.out.println(stack.peek());
            System.out.println(stack.pop());
            System.out.println(stack);
        }
    }
    

    위 예제에서는 Stack 클래스를 사용하여 스택을 구현하고 있습니다. 스택에 세 가지 과일을 추가하고, peek() 메서드를 사용하여 스택의 맨 위에 있는 과일 "orange"을 출력합니다. 또한, pop() 메서드를 사용하여 맨 위에 있는 과일 "orange"을 제거하고 반환합니다. 마지막으로, 스택에 남아있는 모든 요소를 출력합니다.


    Queue

    Queue 인터페이스는 요소를 추가하고 삭제할 때 FIFO 순서를 유지하는 자료구조를 나타내는 인터페이스입니다. Queue 인터페이스는 다음과 같은 메서드를 제공합니다. 

    • add(element) : 지정된 요소를 Queue에 추가합니다. Queue에 더 이상 공간이 없는 경우 IllegalStateException을 throw 합니다.
    • offer(element) : 지정된 요소를 Queue에 추가합니다. Queue에 더 이상 공간이 없는 경우 false를 반환합니다.
    • remove() : Queue에서 맨 앞에 있는 요소를 제거하고 반환합니다. Queue이 비어있는 경우 NoSuchElementException을 throw 합니다.
    • poll() : Queue에서 맨 앞에 있는 요소를 제거하고 반환합니다. Queue이 비어있는 경우 null을 반환합니다.
    • element() : Queue에서 맨 앞에 있는 요소를 반환합니다. Queue이 비어있는 경우 NoSuchElementException을 throw 합니다.
    • peek() : Queue에서 맨 앞에 있는 요소를 반환합니다. Queue이 비어있는 경우 null을 반환합니다.

    Java Collection Framework에서 Queue 인터페이스를 구현하는 클래스로는 LinkedList, PriorityQueue 등이 있습니다. 다음은 Queue 인터페이스를 구현하는 LinkedList 클래스를 사용하는 예제입니다.

    import java.util.LinkedList;
    import java.util.Queue;
    
    public class QueueExample {
        public static void main(String[] args) {
            Queue<String> queue = new LinkedList<String>();
            queue.add("apple");
            queue.add("banana");
            queue.offer("orange");
            System.out.println(queue.element());
            System.out.println(queue.remove());
            System.out.println(queue);
        }
    }

    위 예제에서는 LinkedList 클래스를 사용하여 Queue를 구현하고 있습니다. Queue에 세 가지 과일을 추가하고, element() 메서드를 사용하여 큐의 맨 앞에 있는 과일 "apple"을 출력합니다. 또한, remove() 메서드를 사용하여 맨 앞에 있는 과일 "apple"을 제거하고 반환합니다. 마지막으로, 큐에 남아있는 모든 요소를 출력합니다.
    Queue 자료구조는 대기열을 구현하는 데 매우 유용하며, 여러 프로그래밍 시나리오에서 사용될 수 있습니다. 예를 들어, 메시지 큐, 이벤트 처리, 작업 큐 등에 사용됩니다.

     

    Queue를 LinkedList로 구현하는 이유?

    삽입과 삭제의 성능이 뛰어남
    LinkedList 클래스는 링크드 리스트 구조로 구현되어 있기 때문에, 요소를 삽입하거나 삭제할 때 매우 빠른 성능을 보입니다. 큐에서는 새로운 요소를 뒤쪽에 추가하고, 맨 앞에 있는 요소를 제거하기 때문에 LinkedList 클래스는 큐에 적합한 자료구조입니다.
    메모리를 효율적으로 사용함
    LinkedList 클래스는 링크드 리스트 구조로 구현되어 있기 때문에, 요소들이 연결되어 있습니다. 이러한 구조는 배열과는 달리 메모리를 효율적으로 사용할 수 있습니다. 큐에서는 큐의 크기가 계속해서 변화하기 때문에, 메모리 사용량이 고정된 배열보다는 링크드 리스트가 더 효율적입니다.
    다양한 메서드를 제공함
    LinkedList 클래스는 Queue 인터페이스를 구현하는 데 필요한 메서드를 제공합니다. 따라서, Queue 인터페이스를 구현하기 위해서 LinkedList 클래스를 사용하면 코드를 간결하게 작성할 수 있습니다.

    따라서, LinkedList 클래스는 큐 자료구조를 구현하기 위해 사용하기에 적합한 클래스입니다. 하지만, LinkedList 클래스는 요소를 참조하기 위해 링크드 리스트 구조를 사용하기 때문에, 요소를 검색하는 데는 ArrayList 클래스보다 느린 성능을 보입니다. 따라서, 큐에서 요소의 검색이 자주 일어나는 경우에는 다른 클래스를 사용하는 것이 더 좋습니다.



    Set
    Java Collection Framework에서 Set 인터페이스는 중복되지 않는 데이터를 저장하는 데 사용되는 인터페이스입니다. Set 인터페이스는 중복된 데이터를 허용하지 않으며, 순서가 정해져 있지 않습니다. Set 인터페이스를 구현하는 클래스는 Set 자료구조를 나타내며, 요소의 추가, 제거, 검색 등의 연산을 지원합니다.

     

    Set 인터페이스에서 제공하는 메서드는 다음과 같습니다.

    • add(element) : Set에 요소를 추가합니다. 중복된 요소는 추가되지 않습니다.
    • remove(element) : Set에서 지정된 요소를 제거합니다.
    • contains(element) : Set에 지정된 요소가 포함되어 있는지 확인합니다.
    • size() : Set에 저장된 요소의 개수를 반환합니다.
    • isEmpty() : Set이 비어있는지 확인합니다.
    import java.util.HashSet;
    import java.util.Set;
    
    public class SetExample {
        public static void main(String[] args) {
            Set<String> set = new HashSet<String>();
            set.add("apple");
            set.add("banana");
            set.add("orange");
            System.out.println(set.contains("apple"));
            System.out.println(set.remove("banana"));
            System.out.println(set.size());
            System.out.println(set);
        }
    }

    위 예제에서는 HashSet 클래스를 사용하여 Set을 구현하고 있습니다. Set에 세 가지 과일을 추가하고, contains() 메서드를 사용하여 Set에 "apple"이 포함되어 있는지 확인합니다. 또한, remove() 메서드를 사용하여 Set에서 "banana"를 제거하고 결과를 출력합니다. 마지막으로, Set의 크기와 모든 요소를 출력합니다.

    Set 자료구조는 중복을 허용하지 않는 특성으로 유용하게 사용됩니다. 예를 들어, 중복된 이메일 주소를 제거하거나, 중복된 항목을 제거하는 데 매우 유용합니다.

     


    Map
    Java Collection Framework에서 Map 인터페이스는 키(key)와 값(value)으로 구성된 데이터를 저장하는 데 사용되는 인터페이스입니다. Map 인터페이스는 중복된 키를 허용하지 않으며, 순서가 정해져 있지 않습니다. Map 인터페이스를 구현하는 클래스는 Map 자료구조를 나타내며, 요소의 추가, 제거, 검색 등의 연산을 지원합니다.

     

    Map 인터페이스에서 제공하는 메서드는 다음과 같습니다.

    • put(key, value) : Map에 요소를 추가합니다. 기존에 존재하는 키인 경우, 해당 값(value)을 덮어씁니다.
    • get(key) : Map에서 지정된 키(key)에 해당하는 값을 반환합니다.
    • remove(key) : Map에서 지정된 키(key)에 해당하는 요소를 제거합니다.
    • containsKey(key) : Map에 지정된 키(key)가 포함되어 있는지 확인합니다.
    • containsValue(value) : Map에 지정된 값(value)이 포함되어 있는지 확인합니다.
    • keySet() : Map에 있는 모든 키(key)를 Set으로 반환합니다.
    • values() : Map에 있는 모든 값(value)을 Collection으로 반환합니다.
    • entrySet() : Map에 있는 모든 키(key)와 값(value)을 Entry의 Set으로 반환합니다.

    Java Collection Framework에서 Map 인터페이스를 구현하는 클래스로는 HashMap, TreeMap, LinkedHashMap 등이 있습니다. 다음은 Map 인터페이스를 구현하는 HashMap 클래스를 사용하는 예제입니다.

    import java.util.HashMap;
    import java.util.Map;
    
    public class MapExample {
        public static void main(String[] args) {
            Map<String, Integer> map = new HashMap<String, Integer>();
            map.put("apple", 1000);
            map.put("banana", 2000);
            map.put("orange", 3000);
            System.out.println(map.containsKey("apple"));
            System.out.println(map.get("banana"));
            System.out.println(map.remove("orange"));
            System.out.println(map.keySet());
            System.out.println(map.values());
            System.out.println(map.entrySet());
        }
    }

    위 예제에서는 HashMap 클래스를 사용하여 Map을 구현하고 있습니다. Map에 세 가지 과일과 가격을 추가하고, containsKey() 메서드를 사용하여 "apple"이 포함되어 있는지 확인합니다. 또한, get() 메서드를 사용하여 "banana"의 가격을 출력하고, remove() 메서드를 사용하여 "orange"를 제거하고 결과를 출력합니다. 마지막으로, keySet(), values(), entrySet() 메서드를 사용하여 각각 키, 값, 키-값 쌍의 집합을 출력합니다.
    Map 자료구조는 키-값 쌍의 데이터를 저장하기에 매우 유용하며, 데이터베이스의 키-값 쌍 저장 방식과 유사합니다. Map을 사용하면 다양한 프로그래밍 시나리오에서 데이터를 저장하고 검색하는 데 매우 유용합니다.

Designed by Tistory.