[JAVA] 컬렉션 프레임워크

DANI·2023년 10월 10일
0

JAVA를 공부해보자

목록 보기
12/29
post-thumbnail

📕 컬렉션 프레임워크(collection framework)란?

다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미한다.

즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것

컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현된다.

컬렉션 -> 다수의 데이터 / 프레임워크 -> 표준화 설계



❓ 컬렉션 프레임워크(collection framework)의 이점

  • 가변적인 저장 공간을 제공한다. 고정적인 저장 공간을 제공하는 배열에 대비되는 특징이다.
  • 자료구조, 알고리즘을 구현하기 위한 코드를 직접 작성할 필요 없이, 이미 구현된 컬렉션 클래스를 목적에 맞게 선택하여 사용하면 된다.
  • 데이터 삽입, 탐색, 정렬 등 편리한 API 를 다수 제공하고 제공되는 API 의 코드는 검증되었으며, 고도로 최적화 되어있다.

모든 컬렉션 클래스들은 List, Set, Map중 하나를 구현하고 있다.

📑 Collection

  • List : 순차 자료구조
  • Set : 집합 자료 구조

📑 Map

  • 사전 자료구조
  • 키, 값의 쌍
  • 키 -> 중복X -> 값을 찾기 위함, 반환값은 Set
  • 값 -> 중복 O -> 반환값은 Collection



💡 Collection인터페이스

Collection은 List와 Set의 상위 인터페이스이며, 컬렉션 클래스에 저장된 데이터를 읽고, 추가하고, 삭제하는 등 컬렉션을 다루는데 가장 기본이적인 메서드들을 정의하고 있다.

메서드설명
boolean add(Object o)
boolean addAll(Collection c)
지정된 객체(o) 또는 Collection(c)의 객체들을 Collection에 추가한다.
void clear()Collection의 모든 객체를 삭제한다.
boolean contains(Object o)
boolean containsAll(Collection c)
지정된 객체(o) 또는 Collection의 객체들이 Collection에 포함되어 있는지 확인한다.
boolean equals(Object o)동일한 Collection인지 비교한다.
int hashCode()Collection의 hash code를 반환한다.
boolean isEmpty()Collection이 비어 있는지 확인한다.
Iterator iterator()Collection의 iterator를 얻어서 반환한다.
boolean remove(Object o)지정된 객체를 삭제한다.
boolean removeAll(Collection c)지정된 Collection에 포함된 객체들을 삭제한다.
boolean retainAll(Collection c)지정된 Collection에 포함된 객체만 남기고 다른 객체들은 Collection에서 삭제한다. 이 작업으로 인해 Collection에 변화가 있으면 true를 그렇지 않다면 false를 반환한다.
int size()Collection에 저장된 객체의 개수를 반환한다.
Object[] toArray()Collection에 저장된 객체를 객체배열(Object[])로 반환한다.
Object[] toArray(Object[] a)                                                                    지정된 배열에 Collection의 객체를 지정해서 반환한다.


📖 1. List 인터페이스

  • 인덱스 순서로 요소를 저장한다.
  • 중복된 데이터를 저장할 수 있다.
  • ArrayList, LinkedList, Stack, Vector


📝 1-1. ArrayList

public class ArraList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable {
	...
	transient Object[] elementData;
	...
}
  • ArrayList는 Object배열을 이용해서 데이터를 순차적으로 저장한다.
  • 배열에 더 이상 저장할 공간이 없으면 보다 큰 새로운 배열을 생성해서 기존의 배열에 저장된 내용을 새로운 배열로 복사한 다음에 저장된다.
  • ArrayList는 elementData라는 이름의 Object 배열을 멤버변수로 선언하고 있다.
  • 선언된 배열의 타입이 모든 객체의 최상위 상위 클래스인 Object이기 때문에 모든 종류의 객체를 담을 수 있다.


💾 ArrayListEx1

package List;

import java.util.*;


public class ArrayListEx1 {
    public static void main(String[] args) {

        ArrayList arrayList1 = new ArrayList();
        arrayList1.add(Integer.valueOf(5));
        arrayList1.add(Integer.valueOf(4));
        arrayList1.add(Integer.valueOf(1));
        arrayList1.add(Integer.valueOf(0));
        arrayList1.add(Integer.valueOf(-1));


        // 인덱스 순서로 잘라서 리스트로 저장함
        ArrayList arrayList2 = new ArrayList(arrayList1.subList(1, 4));

        print(arrayList1, arrayList2);
        // list1 : [5, 4, 1, 0, -1]
        // list2 : [4, 1, 0]

        Collections.sort(arrayList1); // 정렬
        Collections.sort(arrayList2); // 정렬

        print(arrayList1, arrayList2);
        // list1 : [-1, 0, 1, 4, 5]
        // list2 : [0, 1, 4]


        // 포함여부 true
        System.out.println(arrayList1.containsAll(arrayList2));


        // 추가
        arrayList2.add("B");
        arrayList2.add("V");
        // 인덱스 3번째 순서에 추가
        arrayList2.add(3, "세번째");
        print(arrayList1, arrayList2);
        // list1 : [-1, 0, 1, 4, 5]
        // list2 : [0, 1, 4, 세번째, B, V]

        // 인덱스 3번째 순서 컨텐츠 변경
        arrayList2.set(3, "Third");
        print(arrayList1, arrayList2);
        // list1 : [-1, 0, 1, 4, 5]
        // list2 : [0, 1, 4, Third, B, V]

        // 겹치는 부분만 남기고 삭제
        // true
        System.out.println(arrayList1.retainAll(arrayList2));
        
        print(arrayList1, arrayList2);
        // list1 : [0, 1, 4]
        // list2 : [0, 1, 4, Third, B, V]


        // arrayList2에서 arrayList1과 동일한 멤버가 있다면 삭제한다.
        // list2.size() - 1 부터 감소시키면서 거꾸로 반복 시켰다.
        // 변수 i를 증가시켜가면서 삭제하면, 한 요소가 삭제될 때마다 빈 공간을 채우기 위해
        // 나머지 요소들이 자리이동을 하기 때문에 올바른 결과를 얻을 수 없다.
        for(int i = arrayList2.size()-1; i>=0; i--){
            if(arrayList1.contains(arrayList2.get(i))){
                arrayList2.remove(i);
            }
        }

        print(arrayList1, arrayList2);
        // list1 : [0, 1, 4]
        // list2 : [Third, B, V]
    }

    private static void print(ArrayList arrayList1, ArrayList arrayList2) {
        System.out.println("list1 : " + arrayList1);
        System.out.println("list2 : " + arrayList2);
    }
}

🔍 ArrayList에서 중간 요소의 삭제가 일어나는 경우

위치를 이동 시킨 후 새로운 배열을 생성하기 때문에 뒤에서부터 삭제하는 것이 좋다.


💾 ArrayListEx2

package List;

import java.util.ArrayList;
import java.util.List;

public class ArrayList2 {
    public static void main(String[] args) {


        final int LIMIT = 10;
        String source = "0123456789abcdefgDFEFDAFDJ!L@#%$%$ZZSDF";

        int length = source.length(); // 문자열의 길이

        // 크기를 여유롭게 설정함
        // 생설할 때 지정한 크기보다 더 많은 객체를 저장하면 자동적으로
        // 크기가 늘어나기는 하지만 이 과정에서 처리시간이 많이 소요되기 때문이다.
        List list = new ArrayList(length / LIMIT + 10);


        // LIMIT 크기만큼 잘라서 리스트에 저장
        for (int i=0; i<length; i+=LIMIT){
            if(i+LIMIT<length){
                list.add(source.substring(i, i+LIMIT));
            }else{
                list.add(source.substring(i));
            }
        }

        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i));
        }
        //
    }
}

ArrayListVector같이 배열을 이용한 자료구조는 데이터를 저장하는데는 효율적이지만, 용량을 변경해야 할 때는 새로운 배열을 생성한 후 기존 배열에서 새로 생성된 배열로 복사해야되기 때문에 상당히 효율이 떨어진다.
따라서 충분한 용량의 인스턴스를 생성하는 것이 좋다.



🔐ArrayList의 단점


크기를 변경하기 힘들다, 비순차적인 데이터의 추가 또는 삭제에 시간이 많이 걸린다.(why? 배열의 중간에 데이터를 추가하려면 빈자리를 만들기 위해 데이터를 복사해서 이동해야한다.)


📝 1-2. LinkedList

  • 불연속적으로 존재하는 데이터를 서로 연결(link)한 형태로 구성되어 있다.
  • 링크드 리스트의 각 요소들은 자신과 연결된 다음 요소에 대한 참조(주소값)와 데이터로 구성되어 있다.

  • 링크드 리스트에서 삭제는 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경하기만 하면 된다.

    단 하나의 참조만 변경하면 삭제가 이루어지므로 배열처럼 데이터를 이동하기 위해 복사하는 과정이 없기 때문에 처리속도가 매우 빠르다.
    버려진 요소는 가비지컬렉터가 수거해간다.


새로운 데이터를 추가할 때는 새로운 요소를 참조변경하기만 하면 되므로 처리 속도가 매우 빠르다.


  • 링크드 리스트는 이동방향이 단방향이기 때문에 다음 요소에 대한 접근은 쉽지만 이전요소에 대한 접근은 어렵다. 이 덤을 보완한 것이 더블 링크드 리스트(이중 연결리스트, doubly linked list)이다.
  • 더블 링크드 리스트는 단순히 링크드 리스트에 참조변수를 하나 더 추가하여 다음 요소에 대한 참조뿐 아니라 이전 요소에 대한 참조가 가능하도록 했을 뿐, 링크드 리스트와 같다.

  • 더블 링크드 리스트의 접근성을 보다 향상시킨 것이 '더블 써큘러 링크드 리스트(이중 원형 연결리스트, doubly circular linked list)'이다. 이는 단순히 더블 링크드 리스트의 첫 번째 요소와 마지막 요소를 서로 연결시킨 것이다.

  • 이렇게 하면 마지막요소의 다음요소가 첫번째 요소가 되고, 첫 번째 요소의 이전 요소가 마지막 요소가 된다.

  • 실제로 구현된 LinkedList 클래스는 이름과 달리 '링크드 리스트'가 아닌 더블 링크드 리스트로 구현되어 있다.



💾 ArrayListLinkedList1

package List;

import java.util.*;


public class ArrayListLinkedList1 {
    public static void main(String[] args) {
        ArrayList al = new ArrayList(200000);
        LinkedList li = new LinkedList();

        System.out.println("==순차적으로 추가하기==");
        System.out.println("ArrayList : " + add1(al));
        System.out.println("LinkedList : " + add1(li));
        System.out.println();

        System.out.println("==중간에 추가하기==");
        System.out.println("ArrayList : " + add2(al));
        System.out.println("LinkedList : " + add2(li));
        System.out.println();

        System.out.println("==중간에서 삭제하기==");
        System.out.println("ArrayList : " + remove1(al));
        System.out.println("LinkedList : " + remove1(li));
        System.out.println();
//
//        System.out.println("==순차적으로 삭제하기==");
//        System.out.println("ArrayList : " + remove2(al));
//        System.out.println("LinkedList : " + remove2(li));
//        System.out.println();
    }

    private static long remove1(List list) {
        long start = System.currentTimeMillis();
        for(int i = list.size()-1; i>=0; i--){
            list.remove(i);
        }
        long end = System.currentTimeMillis();
        return end - start;
    }

    private static long add2(List list) {
        long start = System.currentTimeMillis();
        for(int i = 0; i<10000; i++){
            list.add(500, "X");
        }
        long end = System.currentTimeMillis();
        return end - start;
    }

    private static long add1(List list) {
        long start = System.currentTimeMillis();
        for(int i = 0; i<1000000; i++){
            list.add(i + "");
        }
        long end = System.currentTimeMillis();
        return end - start;
    }

    private static long remove2(List list) {
        long start = System.currentTimeMillis();

        for(int i=0; i<10000; i++){
            list.remove(i);
        }

        long end = System.currentTimeMillis();
        return end - start;
    }

}

🔵 실행 결과(중간 삭제)

==순차적으로 추가하기==
ArrayList : 50
LinkedList : 183

==중간에 추가하기==
ArrayList : 1144
LinkedList : 9

==중간에서 삭제하기==
ArrayList : 6
LinkedList : 17

🔵 실행 결과(순차적 삭제)

==순차적으로 추가하기==
ArrayList : 51
LinkedList : 179

==중간에 추가하기==
ArrayList : 1175
LinkedList : 9

==순차적으로 삭제하기==
ArrayList : 1150
LinkedList : 99

💾 ArrayListLinkedList2

package List;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


public class ArrayListLinkedList2 {
    public static void main(String[] args) {
        ArrayList al = new ArrayList(1000000);
        LinkedList li = new LinkedList();
        add(al);
        add(li);
        System.out.println("==접근 시간 테스트==");
        System.out.println("ArrayList : " + access(al));
        System.out.println("LinkedList : " + access(li));
        System.out.println();


    }

    private static long access(List list) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000; i++) {
            list.get(i);
        }

        long end = System.currentTimeMillis();
        return end - start;
    }

    private static void add(List list) {
        for(int i=0; i<100000; i++){
            list.add(i + "");
        }
    }


}

🔵 실행 결과

==접근 시간 테스트==
ArrayList : 0
LinkedList : 77

  • LinkedList는 불연속적으로 위치한 각 요소들이 서로 연결된 것이라 처음부터 n번째 데이터까지 차례대로 따라가야만 원하는 값을 얻을 수 있다.
  • 따라서 LinkedList는 저장해야하는 데이터의 개수가 많아질수록 데이터를 읽어 오는 시간, 즉 접근시간(access time)이 길어진다는 단점이 있다.


💡 ArrayList vs LinkedList

ArrayListLinkedList
읽기(접근시간)빠름느림
추가/삭제느림빠름
비고순차적인 추가삭제는 더 빠름, 비효율적인 메모리 사용데이터가 많을수록 접근성이 떨어진다


📝 1-3. Stack

  • 스택(Stack)은 마지막에 저장한 데이터를 가장 먼저 꺼내게 되는 LIFO(Last In First Out)구조로 되어 있다.
  • 스택(Stack)은 동전 통과 같은 구조로 양 옆과 바닥이 막혀 있어 한 방향으로만 뺄수 있는 구조이다.
  • 스택(Stack)에 0, 1, 2의 순서로 데이터를 넣으면 꺼낼 때는 2, 1, 0의 순서로 꺼내게 된다. 즉 넣은 순서와 꺼낸 순서가 바뀐다.
  • 스택에는 ArrayList와 간은 배열기반의 컬렉션 클래스가 적합하다.


📖 2. Queue 인터페이스

  • 데이터가 저장된 순서대로 출력되는 선입선출 (FIFO: First In First Out) 의 구조를 갖는 선형 자료구조
  • 큐(Queue)는 양 옆만 막혀 있고, 위 아래로 뚫려 있어서 한 방향으로는 넣고 한 방향으로는 빼는 파이프와 같은 구조로 되어 있다.
  • 큐(Queue)에 0, 1, 2의 순서로 데이터를 넣었다면 꺼낼 때 역시 0, 1, 2 순서로 꺼내게 된다. 순서의 변경 없이 먼저 넣은 것을 먼저 꺼내게 된다.
  • 큐는 ArrayList보다는 데이터의 추가/삭제가 쉬운 LinkedList로 구현하는 것이 더 적합하다.

💾 StackQueue1

package StackQueue;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class StackQueue1 {
    public static void main(String[] args) {
        Stack st = new Stack();
        Queue q = new LinkedList();

        st.push("0");
        st.push("1");
        st.push("2");

        q.offer("0");
        q.offer("1");
        q.offer("2");

        System.out.println("= Stack =");
        
        while(!st.empty()){
            System.out.println(st.pop());
        }

        System.out.println("= Queue =");
        while (!q.isEmpty()){
            System.out.println(q.poll());
        }
    }
}

🔵 실행 결과

= Stack =
2
1
0
= Queue =
0
1
2

📝 1-1. PriorityQueue

  • Queue 인터페이스의 구현체 중에 하나로, 저장한 순서에 관계없이 우선순위(Priority)가 높은 것부터 꺼내게 된다는 특징이 있다.
  • 우선순위는 클래스에 지정된 Comparable 인터페이스의 compareTo에 의해 결정된다.

💾 PriorityQueue1

package StackQueue;

import java.util.PriorityQueue;
import java.util.Queue;

public class PriorityQueue1 {
    public static void main(String[] args) {
        Queue pq = new PriorityQueue();
        pq.offer(15);
        pq.offer(9);
        pq.offer(7);
        pq.offer(8);
        pq.offer(10);

        System.out.println(pq);

        Object obj = null;

        // Queue에서 .poll() 객체를 꺼내서 반환
        while((obj=pq.poll()) != null){
            System.out.println(obj);
        }

        System.out.println(pq);
    }
}


🔵 실행 결과

[7, 8, 9, 15, 10]
7
8
9
10
15
[]

Integer -> 오름차순 정렬 (Comparable 인터페이스 -> int compareTo



📝 1-2. Deque

  • Queue의 변형으로 한 쪽 끝으로만 추가/삭제할 수 있는 Queue와 달리 Deque는 양쪽 끝에 추가/삭제가 가능하다.
    Deque의 상위 인터페이스는 Queue이며, 구현체로는 ArrayDeque와 LinkedList등이 있다.
  • Deque는 Stack과 Queue를 합쳐놓은 것과 같으며 Stack으로 사용할 수도 있고, Queue로 사용할 수도 있다.


📖 3. Set 인터페이스

  • 순서가 없으며, 데이터를 중복하여 저장할 수 없다.
  • 집합 연산 (합집합, 교집합, 차집합 등) 을 지원한다.
  • HashSet, TreeSet


📝 1-1. HashSet

💾 HashSet1

package Set;

import java.util.*;

public class HashSet1 {
    public static void main(String[] args) {
        // Object타입의 배열
        Object[] obj = {"1", Integer.valueOf(5), "2", "4", Double.valueOf(1.5), "1", "2", 5};

        Set set = new HashSet();

        for(int i=0; i<obj.length; i++){
            set.add(obj[i]);
        }

        System.out.println(set);
    }
}

🔵 실행결과

[1, 2, 4, 5, 1.5]

중복이 제거됨


📝 1-2. TreeSet

  • 이진 검색트리(binary search tree)라는 자료구조의 형태로 데이터를 저장하는 컬렉션 클래스
  • 이진 검색트리는 정렬, 검색, 범위검색에 높은 성능을 보이는 자료구조이다.
  • 중복을 허용하지 않고, 정렬된 상태를 유지하므로 단일값 검색과 범위검색이 매우 빠르다.
  • 데이터를 순차적으로 저장하는 것이 아니라 저장위치를 찾아서 저장해야 하고 삭제하는 경우 트리의 일부를 재구성해야 하므로 링크드 리스트보다 데이터의 추가/삭제 시간은 더 걸린다.
  • 배열이나 링크드리스트에 비해 검색과 정렬 기능이 뛰어나다

💡 이진 검색 트리


  • 모든 노드는 최대 두 개의 자식노드를 가질 수 있다.
  • 왼쪽 자식노드의 값은 부모노드의 값보다 작고 오른쪽자식노드의 값은 부모노드의 값모다 커야 한다.
  • 노드의 추가 삭제에 시간이 걸린다.(순차적으로 저장하지 않으므로)
  • 검색(범위검색)과 정렬에 유리하다.
  • 중복된 값을 저장하지 못한다.

💾 TreeSet1

package Set;


import java.util.Set;
import java.util.TreeSet;

public class TreeSet1 {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();

        int[] score = {80, 95, 50, 35, 45, 65, 10, 100};

        for(int i=0; i<score.length; i++){
            set.add(Integer.valueOf(score[i]));
        }
        // .headSet -> 지정된 객체보다 작은 값 반환
        System.out.println("50보다 작은 값 : " + set.headSet(Integer.valueOf(50)));
        // .tailSet -> 지정된 객체보다 큰 값 반환
        System.out.println("50보다 큰 값 : " + set.tailSet(Integer.valueOf(50)));
    }
}

🔵 실행결과

50보다 작은 값 : [10, 35, 45]
50보다 큰 값 : [50, 65, 80, 95, 100]

💾 TreeSet2

package Set;


import java.util.TreeSet;

public class TreeSet2 {
    public static void main(String[] args) {
        TreeSet set = new TreeSet();

        String from = "b";
        String to = "d";

        set.add("abc");
        set.add("BAT");
        set.add("car");
        set.add("Car");
        set.add("dance");
        set.add("dZZZ");
        set.add("fan");

        System.out.println(set);

        System.out.println("range search : from " + from + " to " + to);
        // .subSet -> 범위검색(fromElement와 toElement사이)의 결과를 반환
        // (끝 범위인 toElement는 범위에 포함되지 않음)
        System.out.println("result1 : " + set.subSet(from, to));
        System.out.println("result2 : " + set.subSet(from, to + "zzz"));
    }
}

🔵 실행결과

result1 : [car]
result2 : [car, dZZZ, dance]

"hash"는 검색을 의미함



📖 4. Map 인터페이스

  • Key-value 쌍으로 데이터를 저장한다.
  • 순서가 존재하지 않음.
  • key는 중복을 허용하지 않고, value는 중복을 허용함
  • 기존에 저장된 데이터와 중복된 키와 값을 저장하면 기존의 값은 없어지고 마지막에 저장된 값이 남게된다.
  • HashMap, TreeMap, Hashtable, Properties

📝 4-1. Map.Entry 인터페이스

Map에 저장되는 key-value쌍을 다루기 위해 내부적으로 Entry(데이터) 인터페이스를 정의해 놓은 것

// Map 인터페이스의 소스코드 일부

public interface Map {
	...
	interface Entry {
		Object getKey();
		Object getValue();
		Object setValue(Object value);
		boolean equals(Object o);
		int hashCode();
		...
	}
}

📝 4-2. HashMap, Hashtable

public class HashMap extends AbstractMap implements Map, Cloneable, Serializable {
	transient Entry[] table;
	...
	static class Entry implements Map.Entry {
		final Object key;
		Object value;
		...
	}
	...
}
  • HashMap은 Entry라는 내부 클래스를 정의하고, 다시 Entry타입의 배열을 선언하고 있다.
  • 키(key)와 값(value)은 별개의 값이 아니라 서로 관련된 값이기 때문에 각각의 배열로 선언하기 보다는 하나의 클래스로 정의해서 하나의 배열로 다루는 것이 데이터 무결성(integrity)적인 측면에서 더 바람직하다.
  • HashMpa은 키와 값을 각각 Object 타입으로 저장한다
  • 해싱(hashing)을 사용하기 때문에 많은 데이터를 검색하는데 뛰어난 성능을 보인다.
  • 해싱(hashing)을 사용한 컬렉션 클래스들은 저장순서를 유지하지 않는다.

💾 HashMap1

package Map;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;

public class HashMap1 {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put("dani", "dani");
        map.put("HKD", "1234");
        map.put("Id", "Pw");
        map.put("LLL", "12345");

        Scanner sc = new Scanner(System.in);

        while(true){
            System.out.println("아이디를 입력하세요");
            String id = sc.nextLine().trim(); // .trim() -> 공백제거
            if(map.containsKey(id)) {
                System.out.println("비밀번호를 입력하세요");
                String pw = sc.nextLine().trim();
                if(map.get(id).equals(pw)){
                    System.out.println("로그인 성공!");
                    break;
                }
            }else{
                System.out.println("존재하지 않는 아이디입니다.");
            }
        }
    }
}

🔵 실행결과

50보다 작은 값 : [10, 35, 45]
50보다 큰 값 : [50, 65, 80, 95, 100]

💾 HashMap2

package Map;

import java.util.*;

public class HashMap2 {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put("이병헌", Integer.valueOf(80));
        map.put("김태리", Integer.valueOf(100));
        map.put("유연석", Integer.valueOf(10));

        Set set = map.entrySet(); // Map.Entry타입의 객체로 저장한 Set으로 반환

        // Iterator : 컬렌션에 저장된 요소를 접근하는데 사용하는 인터페이스
        // 컬렉션클래스에 .iterator()를 호출하여 반복문을 통해 클래스의 요소를 읽어온다.
        Iterator iterator = set.iterator();

        System.out.println(set); // 키=값

        // hasNext() -> 읽어 올 요소가 남아 있는지
        while (iterator.hasNext()){

            // next() -> 다음 요소를 읽어온다
            Map.Entry e = (Map.Entry) iterator.next();
            System.out.println("이름 : " + e.getKey() + " 점수 : " + e.getValue());
        }

        set = map.keySet(); // 맵의 key값만 가져옴
        System.out.println("참가자 명단 : " + set);

        Collection values = map.values(); // 맵의 value만 가져옴
        Iterator iterator1 = values.iterator();
        int total = 0;
        while (iterator1.hasNext()){
            Integer i = (Integer) iterator1.next();
            total += i.intValue();
        }
        System.out.println("총점 : " + total);
        System.out.println("평균 : " + (float) total / set.size());
        System.out.println("최고 점수 : " + Collections.max(values));
        System.out.println("최저 점수 : " + Collections.min(values));
    }
}

🔵 실행결과

[김태리=100, 유연석=10, 이병헌=80]
이름 : 김태리 점수 : 100
이름 : 유연석 점수 : 10
이름 : 이병헌 점수 : 80
참가자 명단 : [김태리, 유연석, 이병헌]
총점 : 190
평균 : 63.333332
최고 점수 : 100
최저 점수 : 10

📝 4-2. TreeMap

  • TreeMap은 이진검색트리 형태로 키와 값의 쌍으로 이루어진 데이터를 저장한다
  • 검색과 정렬에 적합한 컬렉션 클래스이다.
  • 검색에 관련한 대부분의 경우에서 HashMap이 TreeMap보다 더 뛰어나므로 HashMap을 사용하는 것이 좋다. 다만 범위 검색이나 정렬이 필요한 경우에는 TreeMap을 사용하는 것이 좋다.

💾 TreeMap1

package Map;


import java.util.*;

public class TreeMap1 {
    public static void main(String[] args) {
        String[] data = {"I", "P","P","P","P","P", "A", "D", "I", "P", "A", "D", "D", "A", "A", "A"};

        TreeMap map = new TreeMap();

        for(int i=0; i<data.length; i++){
            if(map.containsKey(data[i])){
                // 인티저 타입으로 변환
                // .get()은 반환타입이 Object임
                Integer integer = (Integer) map.get(data[i]);
                map.put(data[i], integer + Integer.valueOf(1));
            }else{
                map.put(data[i], Integer.valueOf(1));
            }
        }

        Iterator iterator = map.entrySet().iterator();
        System.out.println("== 기본 정렬 ==");
        while (iterator.hasNext()){
            Map.Entry e = (Map.Entry ) iterator.next();
            int i = ((Integer) e.getValue()).intValue();
            System.out.println(e.getKey() + " : " + printBar('#', i) + " " + i);
        }
        System.out.println();


        Set set = map.entrySet();
        List list = new ArrayList(set);

        Collections.sort(list, new ValueComparator());

        iterator = list.iterator();
        System.out.println("==값의 크기가 큰 순서로 정렬 ==");

        while (iterator.hasNext()){
            Map.Entry e = (Map.Entry ) iterator.next();
            int i = ((Integer) e.getValue()).intValue();
            System.out.println(e.getKey() + " : " + printBar('#', i) + " " + i);

        }

    }

    static class ValueComparator implements Comparator{
        public int compare(Object o1, Object o2){
            if(o1 instanceof Map.Entry<?,?> && o2 instanceof Map.Entry){
                Map.Entry e1 = (Map.Entry) o1;
                Map.Entry e2 = (Map.Entry) o2;

                int v1 = ((Integer)e1.getValue()).intValue();
                int v2 = ((Integer)e2.getValue()).intValue();

                return v2 - v1;
            }
            return -1;
        }
    }
    private static Object printBar(char c, int a) {
        char[] ch = new char[a];
        for(int i=0; i< ch.length; i++){
            ch[i] = c;
        }
        return new String(ch);
    }
}

🔵 실행결과

== 기본 정렬 ==
A : ##### 5
D : ### 3
I : ## 2
P : ###### 6

==값의 크기가 큰 순서로 정렬 ==
P : ###### 6
A : ##### 5
D : ### 3
I : ## 2

💡 intvalue() vs parseInt()


✅ parseInt()

static 이므로 Integer를 생성하지 않는다.
Integer 생성안한다 == 즉 Integer(Object) 라는 박스를 만들지 않고 (래퍼 클래스와 언박싱,박싱) 내용물(String) -> 내용물(int) 교체한다.

✅ intvalue()

static이 아니고 Integer(Object)에서 int로 값을 추출해낸다. 객체(Integer) -> 내용물(int)



📝 4-3. Properties

  • HashMap의 구버전인 Hashtable을 상속받아 구현한 것
  • Hashtable은 키와 값을 (Object, Object)의 형태로 저장하지만 Properties는 (String, String)의 형태로 저장한다.
  • 주로 애플리케이션의 환경 설정과 관련된 속성(property)를 저장하는데 사용되며 데이터를 파일로부터 읽고 쓰는 편리한 기능을 제공한다.

💾 Properties1

package Map;

import java.util.Enumeration;
import java.util.Properties;

public class Properties1 {
    public static void main(String[] args) {
        Properties prop = new Properties();

        // prop에 키와 값을 저장한다.
        prop.setProperty("time", "시간");
        prop.setProperty("apple", "사과");
        prop.setProperty("banana", "바나나");

        // key값을 불러옴
        Enumeration enumeration = prop.propertyNames();

        while (enumeration.hasMoreElements()){
            String s = (String) enumeration.nextElement();
            System.out.println(s + "=" + prop.getProperty(s));
        }
        // apple=사과
        // banana=바나나
        // time=시간

        System.out.println();

        prop.setProperty("time", "시각"); // 값을 변경한다
        System.out.println(prop); // prop 출력
        // {banana=바나나, apple=사과, time=시각}

        prop.list(System.out);// prop에 저장된 요소들을 화면(System.out)에 출력한다.
        // -- listing properties --
        // banana=바나나
        // apple=사과
        // time=시각

    }
}

🔵 실행결과

apple=사과
banana=바나나
time=시간

{banana=바나나, apple=사과, time=시각}
-- listing properties --
banana=바나나
apple=사과
time=시각

💾 PropertiesEx1

/**
 * 1. 작성자 : Dani
 * 2. 작성일 : 12월 10일
 * 3. Properties 기본 문법
 *  a.HashMap의 구버전인 Hashtable을 상속받아 구현한 것
 *  b.Properties는 (String, String)의 형태로 저장
 *  c.애플리케이션의 환경 설정과 관련된 속성(property)를
 *    저장하는데 사용되며 데이터를 파일로 부터 읽고 쓰는 편리한 기능을 제공
 */
package Basics;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PropertiesEx1 {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        prop.setProperty("key1", "value1");
        prop.setProperty("key2", "value2");
        prop.setProperty("key3", "value3");
        prop.list(System.out);

        // 파일을 생성함
        prop.store(new FileWriter("test.properties"), "(❁´◡`❁)");
        prop.storeToXML(new FileOutputStream("test.xml"), "(❁´◡`❁)");

        // 파일을 읽어옴
        prop.loadFromXML(new FileInputStream("test.xml"));
        prop.list(System.out);
    }
}

🔵 실행결과

-- listing properties --
key1=value1
key2=value2
key3=value3
-- listing properties --
key1=value1
key2=value2
key3=value3

파일이 생성된다.


🔍 Collections

  • Arrays가 배열과 관련된 메서드를 제공하는 것처럼, Collections는 컬렉션과 관련된 메서드를 제공한다. java.util.Collections
  • fill(), copy(), sort(), binarySearch()등의 메서드는 두 클래스 모두 포함되어 있고 같은 기능을 한다.

편의기능이라 static이며 final클래스로 되어 있다. 상속을 하지 않음


📑 컬렉션의 동기화

static Collection synchronizedCollection(Collection c)
static List synchronizedList(List list)
static Set sychronizedSet(Set s)
static Map sychronizedMap(Map m)
static SortedSet synchronizedSortedSet(SortedSet s)
static SortedMap synchronizedSortedMap(SortedMap m)

멀티쓰레드 프로그래밍에서 여러 쓰레드가 하나의 객체에 동시에 접근할 수 있기 때문에 데이터의 일관성을 유지하기 위해서 동기화가 필요하다.


📑 변경불가 컬렉션 만들기

static Collection unmodifiableCollection(Collection c)
static List unmodifiableList(List list)
static Set unmodifiableSet(Set s)
static Map unmodifiableMap(Map m)
static NavigableSet unmodifiableNavigableSet(NavigableSet s)
static SortedSet unmodifiableSortedSet(SortedSet s)
static NavigableMap unmodifiableNavigableMap(NavigableMap m)
static SortedMap unmodifiableSortedMap(SortedMap m)

📑 싱글톤 컬렉션 만들기

static List singletonList(Object o)
static Set singleton(Object o)
static Map singletonMap(Object key, Object value)

📑 한 종류의 객체만 저장하는 컬렉션 만들기

static Collection checkedCollection(Collection c, Class type)
static List checkedList(List list, Class type)
static Set checkedSet(Set s, Class type)
static Map checkedMap(Map m, Class keyType, Class valueType)
static Queue checkedQueue(Queue queue, Class type)
static NavigableSet checkedNavigableSet(NavigableSet s, Class type)
static SortedSet checkedSortedSet(SortedSet s, Class type)
static NavigableMap checkedNavigableMap(NavigableMap m, Class keyType, Class valueType)
static SortedMap checkedSortedMap(SortedMap m, Class keyType, Class valueType)

참고 :
https://www.tcpschool.com/java/java_collectionFramework_concept

https://hudi.blog/java-collection-framework-1/

https://velog.io/@kai6666/Java-%EC%BB%AC%EB%A0%89%EC%85%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-Collection-Framework

https://coding-factory.tistory.com/550

https://inpa.tistory.com/entry/JCF-%F0%9F%A7%B1-Collections-Framework-%EC%A2%85%EB%A5%98-%EC%B4%9D%EC%A0%95%EB%A6%AC#hashmap_%ED%81%B4%EB%9E%98%EC%8A%A4

https://wikidocs.net/122193#get

https://escapefromcoding.tistory.com/787

https://onlyfor-me-blog.tistory.com/319

0개의 댓글