[JAVA] 컬렉션 프레임워크

JUJU·2024년 1월 30일

배열은 같은 타입의 데이터를 연속된 공간에 나열한 자료구조이다. 클래스 타입의 배열을 생성해서 객체를 저장할 수도 있다. 하지만, 배열 선언시 할당한 크기를 변경 불가능하고, 항목을 추가, 삭제 하는 등의 메소드도 직접 구현해야 한다.

컬렉션 프레임워크는 이러한 불편함을 해결한다.

✏️ 컬렉션 프레임워크

컬렉션(Collection)은 객체의 저장을 뜻하고, 프레임워크(Framework)란 사용 방법을 저장해놓은 라이브러리를 말한다.

간단히 말하자면, 자료구조와 메소드를 미리 구현해놓은 인터페이스와 각 인터페이스에 대응하는 구현 클래스의 모음이다.

  • 컬렉션 프레임워크의 주요 인터페이스로는 List, Set, Map이 있다.
  • 컬렉션 프레임워크는 크기를 동적으로 조절한다.
  • 컬렉션은 객체만 저장한다. 기본 자료형은 Wrapper 클래스로 객체로 만들어줘야 저장할 수 있다.
  • List와 Set은 Collection 인터페이스를 상속하지만, Map은 독립적이다.

자료구조와 직접적으로 연관이 있으므로, 2학년 2학기 때 공부했던 자료구조 스터디 노트를 참고하면 더 쉽게 이해할 수 있을 것 같다. https://github.com/jaewon-ju/Study-Notes/tree/master/DATA_STRUCTURE


✏️ List 컬렉션

List 컬렉션은 배열과 비슷하게 객체를 인덱스로 관리한다. 하지만, 저장공간이 동적으로 증가하며, 추가, 삭제, 검색 등의 메소드가 제공된다.

  • List 컬렉션은 객체의 번지를 참조한다.
  • 동일한 객체를 중복 저장할 수 있다.

List 인터페이스를 구현한 구현 클래스로는 ArrayList, Vector, LinkedList 등이 있다.
List 인터페이스의 메소드는 다음과 같은 것들이 있다.

// --- 객체 추가 ---
boolean add(E element) // 맨 끝에 추가
void add(int index, E element) // index에 추가
E set(int index, E element) // index 위치의 값을 element로 대체
// --- 객체 검색 ---
boolean contains(Object o) // 객체가 있는지 확인
E get(int index) // index 위치에 있는 객체 반환
int size() // 저장된 객체 수 반환
// --- 객체 삭제 ---
void clear() // 저장된 모든 객체 삭제
E remove(int index) // 해당 위치의 객체 삭제

■ ArrayList

List<E> list = new ArrayList<E>();

E에는 저장할 객체의 타입을 넣는다. 예를 들어 String 타입 객체를 저장하는 경우 다음과 같이 작성한다.

List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<>(); // ArrayList의 타입 파라미터를 생략하면 자동으로 List 타입을 따라감

  • 기본 생성자로 ArrayList 객체를 생성하면 10개의 객체를 저장할 수 있는 초기공간을 갖는다.
  • ArrayList에서 특정 index의 객체를 제거하면, 객체를 하나씩 앞당겨야 한다.
    ➜ 배열로 구현한 연결리스트와 비슷하다. 즉, 굉장히 비효율적이다.

ArrayList 사용예시

List<String> list = new ArrayList<>();

list.add("JAVA");
list.add("Programming");
list.add("Nice");

int size = list.size();
String str = list.get(2); // Nice 반환

list.remove(1); // Programming 삭제 후 한 칸씩 땡기기
list.remove(1); // Nice 삭제

■ Vector

List<E> list = new Vector<E>();
  • Vector는 ArrayList와 동일한 구조를 가지고 있다.
  • ArrayList와의 차이점은, Vector는 동기화된 메소드로 구성되어 있다는 것이다.
  • 멀티 스레드가 동시에 Vector의 메소드를 실행할 수 없다.

■ LinkedList

ArrayList와 Vector가 배열로 구현한 연결리스트라면, LinkedList는 포인터로 구현한 연결리스트이다. (이해를 위한 설명일 뿐이다. JAVA에는 포인터가 없다.)

List<E> list = new LinkedList<E>();
  • ArrayList와 Vector는 데이터가 연속적으로 존재하지만, LinkedList는 데이터가 불연속적으로 존재한다.
  • LinkedList는 각 노드가 앞 뒤 노드의 참조를 가지고 있다.
  • 삽입/삭제할 때 ArrayList 보다 유리하다.


✏️ Set 컬렉션

Set 컬렉션은 집합과 비슷하다. 저장 순서가 유지되지 않고, 중복해서 저장할 수 없기 때문이다.

  • 객체를 중복 저장할 수 없다.
  • 하나의 null만 저장할 수 있다.

Set 인터페이스를 구현한 구현 클래스로는 HashSet, LinkedHashSet, TreeSet 등이 있다.
Set 인터페이스의 메소드는 다음과 같은 것들이 있다.

// --- 객체 추가 ---
boolean add(E element) 
// --- 객체 검색 ---
boolean contains(Object o) // 객체가 있는지 확인
Iterator<E> iterator() // 지정된 객체를 한 번씩 가져오는 반복자를 리턴
int size() // 저장된 객체 수 반환
// --- 객체 삭제 ---
void clear() // 저장된 모든 객체 삭제
boolean remove(Object o) // 해당 객체 삭제

Set 컬렉션은 인덱스가 없기 때문에, 특정 객체를 지정해서 가져오려면 반복자(Iterator)를 사용해야 한다.

//반복자 사용법

Set<String> set = new HashSet<>();
Iterator<String> iterator = set.iterator();// Iterator는 인터페이스 타입이다.

while(iterator.hasNext()){ //가져올 객체가 있으면 true
	String str = iterator.next(); //다음 객체를 가져옴
    
    if(str.equals("ju"))
    	iterator.remove(); //실제 Set에서도 삭제됨
}

■ HashSet

HashSet은 Set 인터페이스의 구현 클래스이다.

Set<E> set = new HashSet<E>();
  • HashSet은 객체들을 순서 없이 저장한다.
  • 동일한 객체는 중복 저장하지 않는다.
    ⚠️ HashSet이 판단하는 동일한 객체란?
    ➜ HashCode() 리턴값이 같고 equals의 결과가 true인 두 객체

    이전 포스팅에서 이 주제를 다룬적이 있었다.
    https://velog.io/@jaewon-ju/JAVA-java.lang-%ED%8C%A8%ED%82%A4%EC%A7%80
  • HashSet에 저장될 객체는 반드시 equals()와 hashCode()를 재정의해야한다.

HashSet 사용예시
Set<String> set = new HashSet<>();

set.add("JAVA");
set.add("Programming");
set.add("JAVA"); // 중복이므로 한번만 저장됨
set.add("JVM");

for(String str : set){
	System.out.println(str); // 반복자를 사용하지 않고 객체를 얻는 방법도 있다.
}

set.clear();


✏️ Map 컬렉션

Map 컬렉션은 키와 값으로 구성된 Map.Entry 객체를 저장한다.
Entry는 Map 인터페이스 내부에 선언된 중첩 인터페이스이다.

  • 키와 값은 모두 객체이다.
  • 키는 중복 저장 불가, 값은 중복 저장 가능
  • 기존에 저장된 키와 동일한 키로 값을 저장하면, 기존 값은 없어지고 새로운 값이 저장된다.



Map 인터페이스를 구현한 구현 클래스는 HashMap, Hashtable 등이 있다.
Map 인터페이스의 메소드는 다음과 같은 것들이 있다.

// --- 객체 추가 ---
V put(K key, V value) // 주어진 키로 값을 저장. 새로운 키일 경우 null, 동일한 키인 경우, 이전 value를 리턴
// --- 객체 검색 ---
boolean containsKey(Object key) // 키 객체가 있는지 확인
boolean containsValue(Object value) // 값 객체가 있는지 확인

Set<Map.Entry<K,V>> entrySet() // 모든 Map.Entry 객체를 Set에 담아 리턴
Set<K> keySet() // 모든 키를 Set 객체에 담아 리턴
V get(Object key) // 주어진 키가 가진 값을 리턴

int size() // 저장된 객체 수 반환
// --- 객체 삭제 ---
void clear() // 저장된 모든 객체 삭제
V remove(Object key) // 주어진 key에 해당되는 Map.Entry 객체 삭제 후 값 리턴

■ HashMap

HashMap은 Map 인터페이스의 구현 클래스이다.

Map<K,V> map = new HashMap<>();
  • HashMap의 키로 사용할 객체는 hashCode()와 equals() 메소드를 반드시 재정의해야 한다.
  • 키와 값의 타입은 기본 타입을 사용할 수 없고, 클래스 및 인터페이스 타입만 가능하다.

HashMap 사용방법

Map<String, Integer> map = new HashMap<>();

map.put("ju", 100);
map.put("shin", 95);
map.put("choi", 90);

System.out.println(map.get("ju")); // 100 출력

HashMap에서 객체의 처리법

1. keySet()

Set<String> keySet = map.keySet(); // 키 객체들을 Set으로 반환함
Iterator<String> keyIterator = keySet.iterator(); // 해당 Set의 반복자 생성

while(keyIterator.hasNext()){
	String key = keyIterator.next(); // key 값을 하나씩 가져옴
    Iteger value = map.get(key); // key 값에 해당되는 value 값을 하나씩 가져옴
}

2. entrySet()

// entrySet은 Map.Entry 객체를 Set에 담아서 반환한다.
Set<Map.Entry<String,Integer>> entrySet = map.entrySet();

// 반복자 생성. 저장할 객체는 Map.Entry 객체
Iterator<Map.Entry<String, Integer>> entryInterator = entrySet.iterator(); 

while(entryIterator.hasNext()){
	Map.Entry<String, Integer> mapEntry = entryIterator.next();
    String key = mapEntry.getKey(); // Map.Entry 객체의 메소드
    Integer value = mapEntry.getValue();
}

■ Hashtable

HashMap은 Map 인터페이스의 구현 클래스로, HashMap과 동일한 내부 구조를 가지고 있다.

Map<K,V> map = new Hashtable<>();
  • HashMap과 Hashtable의 차이점은, ArrayList와 Vector의 차이점과 같다.
  • 즉, Hashtable은 synchronized 메소드로 구성되어 있다는 것이다.
  • 멀티 스레드가 동시에 Hashtable의 메소드를 실행할 수 없다.


✏️ LIFO와 FIFO 컬렉션

LIFO(Last In First Out): 후입 선출 구조
FIFO(First In First Out): 선입 선출 구조

■ Stack 클래스

Stack은 LIFO 자료구조를 구현한 클래스이다.

Stack<E> stack = new Stack<>();
Stack<String> stack = new Stack<>();

// 객체 추가
stack.push("ju");
stack.push("shin");
stack.push("choi");

// 객체 가져오기(삭제 안함)
String str = stack.peek(); // choi를 str에 저장

// 객체 가져오기(삭제 함)
String str = stack.pop(); // choi를 str에 저장

■ Queue 인터페이스

Queue는 FIFO 자료구조에서 사용되는 메소드를 정의한인터페이스이다.

Queue 인터페이스를 구현한 클래스는 LinkedList가 있다.

Queue<String> queue = new LinkedList<>();

// 객체 추가
queue.offer("ju");
queue.offer("shin");
queue.offer("choi");

// 객체 가져오기(삭제 안함)
String str = queue.peek(); // ju를 str에 저장

// 객체 가져오기(삭제 함)
String str = queue.poll(); // ju를 str에 저장


REFERENCE

혼자 공부하는 자바

profile
백엔드 개발자

0개의 댓글