공부/Java

[즐거운자바](9) - 컬렉션 프레임워크

다음에바꿔야지 2023. 10. 18. 16:00

<컬렉션 프레임워크>

데이터 그룹을 저장, 조작, 검색, 조작하기 위한 클래스와 인터페이스의 집합.

컬렉션 프레임워크는 자료구조를 구현하는 다양한 클래스들을 제공하여 데이터를 저장하고 처리하는 데 사용된다.

<장점>

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


<구성 요소>

1. 인터페이스: 컬렉션 프레임워크의 핵심 요소로, 다양한 유형의 컬렉션을 정의하는데 사용. 대표적으로 `Collection`, `List`, `Set`, `Map` 등.

2. 구현 클래스: 인터페이스를 구현하는 다양한 클래스들로, 각각의 클래스는 특정 유형의 데이터 구조를 구현. 예를 들면, `ArrayList`, `LinkedList`, `HashSet`, `HashMap` 등.

3. 알고리즘: 데이터를 조작하고 처리하는 데 사용되는 여러 알고리즘 제공. 검색, 정렬, 삽입, 삭제 등과 같은 다양한 작업을 수행.

<종류>

  • 리스트 (List) : 인덱스 순서로 요소를 저장한다. 중복된 데이터를 저장할 수 있다.
  • 큐 (Queue) : 데이터가 저장된 순서대로 출력되는 선입선출(FIFO: First In First Out) 의 구조를 갖는 선형 자료구조이다.
  • 집합 (Set) : 순서가 없으며, 데이터를 중복하여 저장할 수 없다. 집합 연산 (합집합, 교집합, 차집합 등) 을 지원한다.
  • 맵 (Map) : Key-value 쌍으로 데이터를 저장한다. 순서가 존재하지 않으며, Key 가 중복될 수 없다.

 

<계층>

출처:&nbsp;https://hudi.blog/java-collection-framework-1/

<핵심 인터페이스>

  • Collection: 모든 것을 꺼내는 것을 표현한 인터페이스
    • iterator(): 꺼내는 것을 표현한 인터페이스
  • List: 객체를 담아두기 위한 자료 구조. 순서 기억.
  • Set: 중복을 허용하지 않는 자료 구조.
  • Map: key와 value로 구성되어 잇는 자료 구조. 저장할때 push(key, value)로 저장.

java.util.Collection

컬렉션 프레임워크에서 가장 기본이 되는 인터페이스.

순서를 기억하지 않고, 중복을 허용하여 자료를 다루는 목적.

 

java.util.List

순서가 중요한 자료를 다룰 때 사용.

Collection을 상속받음으로써 add(), size(), iterator()메소드 사용 가능.

특정 순서로 저장된 자료를 꺼낼 수 잇는 get(int) 메소드를 가지고 있다.

 

java.util.Set

중복을 허용하지 않는 자료를 다룰 때 사용하는 인터페이스.

같은 값을 여러 번 추가해도 마지막 값 하나만 저장됨.

Set 인터페이스에 저장되는 객체들은 Object가 가지고 있는 equals()메소드와 hashCode()메소드를 오버라이딩해야 한다.

 

java.util.Iterator

자료 구조에서 자료를 꺼내기 위한 목적.

Iterator 패턴을 구현하고 있다.

 

java.util.Map

키(key)와 값(value)을 함께 저장하기 위한 목적.

같은 key값으로 하나의 값만 저장 가능.

 

<제네릭 사용 차이>

Generic을 사용하지 않고 ArrayList 사용하기

import java.util.ArrayList;

public class ListExam01 {
	public static void main(String[] args) {
    	
        //자료구조 객체들은 제네릭을 사용하지 않으면 Object 타입을 저장한다. 
        ArrayList list = new ArrayList();
        list.add("kim");
        list.add("lee");
        list.add("hong");
        
        String str1 = (String)list.get(0);
        String str2 = (String)list.get(1);
        String str3 = (String)list.get(2);
        
        System.out.println(srt1);
        System.out.println(srt2);
        System.out.println(srt3);
    }
}

Generic을 사용하고 ArrayList 사용하기

import java.util.ArrayList;

public class ListExam02 {
	public static void main(String[] args) {        
        ArrayList<String> list = new ArrayList<>();
        list.add("kim");
        list.add("lee");
        list.add("hong");
        
        String str1 = list.get(0);
        String str2 = list.get(1);
        String str3 = list.get(2);
        
        System.out.println(srt1);
        System.out.println(srt2);
        System.out.println(srt3);
    }
}

Generic을 사용하고 Collection, Iterator 사용하기

import java.util.ArrayList;
import java.util.Colleciton;
import java.util.Iterator;

public class ListExam03 {
    public static void main(String[] args) {        
        Collection<String> collection = new ArrayList<>();
        collection.add("kim");
        collection.add("lee");
        collection.add("hong");

        System.out.println(collection.size());

        Iterator<String> iter = collection.iterator();
        while (iter.hasNext()) {
            String str = iter.next();
            System.out.println(str);
        }
    }
}

Generic을 사용하고 List 사용하기

import java.util.ArrayList;

public class ListExam02 {
	public static void main(String[] args) {
    	// 앞에는 인터페이스 타입, 뒤에는 인터페이스를 구현하고 있는 인스턴스로 작성
        List<String> list = new ArrayList<>();
        list.add("kim");
        list.add("lee");
        list.add("hong");
        
        String str1 = list.get(0);
        String str2 = list.get(1);
        String str3 = list.get(2);
        
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
    }
}

왜 'ArrayList<String> list = new ArrayList<>();' 를 사용하지 않고, 'List<String> list = new ArrayList<>();'를 사용하였나?

코드의 유연성과 확장성을 고려할 때, 인터페이스를 사용하는 것이 더 좋다. 왜냐하면 List는 '인터페이스'이고, ArrayList은 그 인터페이스의 '구현 클래스'이기 때문이다. 따라서 나중에 코드를 변경할 때 ArrayList를 다른 리스트 구현체로 쉽게 바꿀 수 있다.

ArrayList<String> list = new ArrayList<>();는 구체적인 클래스를 직접 사용하는 것이다. 이는 코드를 작성할 때 ArrayList만 사용할 것이라는 것을 확신할 때 사용하는 것이 좋다.

 

Generic을 사용하고 Set 사용하기

import java.util.*

public class SetExam {
    public static void main(String[] args) {        
        Set<String> set = new HashSet<>();
        set.add("hello");
        set.add("hi");
        set.add("hong");

        Iterator<String> iter = set.iterator();
        
        while (iter.hasNext()) {
            String str = iter.next();
            System.out.println(str);
        }
    }
}
import java.util.*

public class SetExam2 {
    public static void main(String[] args) {
    	Set<MyData> mySet = new HashSet<>;
        
        mySet.add(new MyData("kim", 500));
        mySet.add(new MyData("lee", 200));
        mySet.add(new MyData("hong", 700));
        
        Iterator<MyData> iter = mySet.iterator();
        while (iter.hasNext()) {
            MySet mySet = iter.next();
            System.out.println(mySet);
        }
    }
}

class MyData {
    private String name; 
    private int value;
    
    pulbic MyData(String name, int value) {
    	this.name = name;
        this.value = value;
    }
    
    public String getName() {
    	return name;
    }
    
    public int getValue() {
    	return value;
    }
    
    @Override
    pulbic boolean equals(Object o) {
    	if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyData myData = (MyData) o;
        return value == myData.value && Objects.equals(name, myData.name);
    }
    
    @Override
    public int hashCode() {
    	return Objects.hash(name, value);
    }
    
    @Override
    public String toString() {
    	return "MyData{" + 
        		"name ='" + name + '\'' +
                ", value = " + value +
                '}';
    }
}
hash 알고리즘이 나오면 오브젝트가 가지고 있는 hashCode, equals 두가지 메소드를 반드시 오버라이딩 해줘야 한다.

 

Generic을 사용하고 Map 사용하기

import java.util.Map;

public class MapExam2 {
	public static void main (String[] args) {
    	Map<String, String> map = new Map<>():
        
        map.put("k1", "hello");
        map.put("k2", "hi");
        map.put("k3", "안녕");
        
        set<String> keySet = map.keySet();
        Iterator<String> iterator = keySet.iterator();
        while(iterator.hashNext()) {
        	String key = iterator.next();
            String value = map.get(key)        
	        System.put.println(key + ":" + value);
        }
    }
}

Map의 key도 유일한 값을 가져야하기 때문에 hash 알고리즘을 사용해 equals(), hashCode()가 구현되어야 한다.

같은 key, value가 있으면 기존 value를 덮어 씌운다.

 

set에서 꺼내려면 iterator을 사용.

map에서 꺼내려면 key를 이용.

 

Collection.sort(): 정렬

import java.util.ArrayList;
import java.util.Collections;
import java.uitl.List;

public class SortExam {
	public static void main (String[] args) {
    	List<String> list = new ArrayList<>():
        
        liist.add("kim");
        liist.add("lee");
        liist.add("hong");
        
        Collections.sort(list);
        
        for (int i = 0; i < list.size(); i++) {
        	System.out.println(list.get(i));
        }
    }
}