스터디/혼공학습단

[혼공학습단] 알고리즘을 준비할려면 꼭 알아야하는 컬렉션 프레임워크

Jop 2023. 2. 11. 23:57
반응형

벌써 혼공학습단 5주차라니...스터디도 얼마 안남았네요..
유종의 미를 거둬보겠습니다. 🥹
오늘 공부할 단원은 13. 컬렉션 프레임워크 입니다.
딱 주제를 보자마자 요즘 코딩테스트 준비하는 저에게 꼭 공부해야되는 단원이라 생각이 들어서
개념을 열심히 다졌습니다.

📄 혼공자 5주차 미션 (Chapter 13. 컬렉션 프레임워크)

- 기본미션 : 직접 정리한 키워드 공유하기
- 선택미션 : p. 573 [직접 해보는 손코딩] 코딩 과정 및 실행 결과 캡쳐하기

컬렉션 프레임워크

자바는 자료구조를 사용해서 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 인터페이스와 구현 클래스를 java.util 패키지에서 제공합니다. 이들을 총칭해서 컬렉션 프레임워크라고 부릅니다.

List 컬렉션

List 컬렉션은 배열과 비슷하게 객체를 인덱스로 관리합니다. 배열과의 차이점은 저장 용량(capacity)이 자동으로 증가하며, 객체를 저장할 때 자동 인덱스가 부여된다는 것입니다.
또한 객체 자체를 저장하는 것이 아니라 다음 그림과 같이 객체의 번지를 참조합니다. 그렇기 때문에 동일한 중복 저장을 할 수 있는데, 이 경우 동일한 번지가 참조됩니다. null도 저장이 가능하며 이 경우 해당 인덱스는 객체를 참조하지 않습니다.

객체의 번지 참조하는 List 컬렉션
제네릭스는 선언할 수 있는 타입이 객체 타입입니다. int는 기본자료형이기 때문에 들어갈수 없으므로 int를 객체화시킨 wrapper클래스를 사용해야 합니다.
  • List 컬렉션에서 공통적으로 사용 가능한 List 인터페이스 메소드 (ArrayList, Vector, LinkedList ...)

* 매개 변수 타입과 리턴 타입에 E라는 타입 파라미터가 있는데, 이것은 저장되는 객체의 타입을 List컬렉션을 생성할 때 결정하라는 뜻!

기능 메소드 설명
객체 추가 boolean add(E e) 주어진 객체를 맨 끝에 추가합니다.
void add(int index,E element) 주어진 인덱스에 객체를 추가합니다.
E set(int index, E element) 주어진 인덱스에 저장된 객체를 주어진 객체로 바꿉니다.
객체 검색 boolean contains(Object o) 주어진 객체가 저장되어 있는지 조사합니다.
E get(int index) 주어진 인덱스에 저장된 객체를 리턴합니다.
boolean isEmpty() 컬렉션이 비어 있는지 조사합니다.
int size() 저장되어 있는 전체 객체 수를 리턴합니다.
객체 삭제 void clear() 저장된 모든 객체를 삭제합니다.
E remove(int index) 주어진 인덱스에 저장된 객체를 삭제합니다.
boolean remove(Object o) 주어진 객체를 삭제합니다.

ArrayList

ArrayList는 List 인터페이스의 대표적인 구현 클래스입니다.

ArrayList를 생성하기 위해서는 저장할 객체 타입을 E 타입 파라미터 자리에 표기하고 기본 생성자를 호출하면 됩니다.

* Java7에서는 코드의 장황함을 줄이기 위해 다이아몬드 연산자(<>)를 도입했습니다. 그러므로 Java7이후부턴 List유형을 선언할 필요 없이 <>을 사용하여 생성자선언을 단순화 할 수 있습니다. 그러므로 위의 코드에서 2번째 줄 코드를 사용하는게 CodeSmell을 피할 수 있습니다.

기본 생성자로 ArrayList 객체를 생성하면 내부에 10개의 객체를 저장할 수 있는 초기 용량(capacity)을 가지게 됩니다.
*초기용량을 변경하고 싶을 경우, 파라미터에 초기용량을 설정하면 됩니다.

Vector

Vector는 ArrayList와 동일한 내부 구조를 가지고 있습니다. Vector를 생성하기 위해서는 저장할 객체 타입을 타입 파라미터로 표기하고 기본 생성자를 호출하면 됩니다.

ArrayList와 다른 점은 Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Vector의 메소드들을 실행할 수 없고, 하나의 스레드가 메소드를 실행 완료해야만 다른 스레드가 메소드를 실행할 수 있다는 것 입니다. 그래서 멀티 스레드 환경에서 안전하게
객체를 추가, 삭제할 수 있습니다.

LinkedList

LinkedList는 List 구현 클래스이므로 ArrayList와 사용 방법은 똑같은데, 내부 구조는 완전히 다릅니다.
ArrayList는 내부 배열에 객체를 저장해서 관리하지만, LinkedList는 인접 참조를 링크해서 체인처럼 관리합니다.

LinkedList에서 특정 인덱스의 객체를 제거하면 앞뒤 링크만 변경되고 나머지 링크는 변경되지 않습니다.

Set 컬렉션

List 컬렉션은 객체의 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않습니다. 또한 객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있습니다. Set 컬렉션은 순서가 상관이 없고, 중복이 허용되지 않습니다.

  • Set컬렉션에서 공통으로 사용 가능한 Set 인터페이스 메소드 (HashSet, LinkedHashSet, TreeSet ...)

* Set은 인덱스로 관리하지 않기 때문에 인덱스를 매개값으로 갖는 메소드가 없습니다.

기능 메소드 설명
객체 추가 boolean add(E e) 주어진 객체를 저장합니다. 객체가 성공적으로 저장되면 true를 리턴하고 중복 객체면 false를 리턴합니다.
객체 검색 boolean contains(Object o) 주어진 객체가 저장되어 있는지 조사합니다.
boolean isEmpty() 컬렉션이 비어 있는지 조사합니다.
Iterator<E> iterator() 저장된 객체를 한 번씩 가져오는 반복자를 리턴합니다.
int size() 저장되어 있는 전체 객체 수를 리턴합니다.
객체 삭제 void clear() 저장된 모든 객체를 삭제합니다.
boolean remove(Object o) 주어진 객체를 삭제합니다.


Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없습니다. 대신, 전체 객체를 대상으로 한 번씩 반복해서 가져오는 반복자(iterator)를 제공합니다. 반복자는 Iterator 인터페이스를 구현한 객체를 말하는데, iterator() 메소드를 호출하면 얻을 수 있습니다.

  • Set 컬렉션의 객체 저장및 삭제 메소드
Set<String> set = ...;
set.add("홍길동");	   // 객체 추가
set.add("신용권");
set.remove("홍길동"); // 객체 삭제
  • Iterator 인터페이스에 선언된 메소드
리턴타입 메소드 설명
boolean hasNext() 가져올 객체가 있으면 true를 리턴하고 없으면 false를 리턴
E next() 컬렉션에서 하나의 객체를 가져옵니다.
void remove() Set 컬렉션에서 객체를 제거

HashSet

HashSet은 Set 인터페이스의 구현 클래스입니다. HashSet을 생성하기 위해서는 다음과 같이 기본 생성자를 호출하면 됩니다.

Set<E> set = new HashSeet<E>();

타입 파라미터 E에는 컬렉션에 저장할 객체 타입을 지정하면 됩니다. 예를 들어 String 객체를 저장하는 HashSet은 다음과 같이 생성할 수 있습니다.

Set<String> set = new HashSet<String>();
Set<String> set = new HashSet<>();
// HashSet의 E 타입 파라미터를 생략하면 왼쪽 Set에 지정된 타입을 따라 감

HashSet은 객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않습니다.
객체를 저장하기 전에 먼제 객체의 hashCode()메소드를 호출해서 해스코드를 얻어내고, 이미 저장되어 있는 객체들의 해시코드와 비교합니다. 만약 동일한 해시코드가 있다면 다시 equals()메소드로 두 객체를 비교해서 true가 나오면 동일한 객체로 판단하고 중복 저장하지 않습니다.

사용자 정의 클래스를 이용할 경우, 객체의 동등비교가 어려워지는데 이때 hashCode()와 equals()메소드를 재정의하여 클래스의 유무 판단을 할 수 있습니다.

  • hashCode()와 equals() 메소드 재정의
import java.util.HashSet;
import java.util.Set;

class Member{
    String name;
    int age;

    Member(String name, int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj){
        if(obj instanceof Member){
            Member member = (Member) obj;
            return member.name.equals( name ) && (member.age == age);
        }else{
            return false;
        }
    }

    @Override
    public int hashCode(){
        return name.hashCode() + age;
    }
}

public class HashSetExample2 {

    public static void main ( String[] args ) {
        Set<Member> set = new HashSet<>();

        set.add( new Member( "홍길동",30 ) );
        set.add( new Member( "홍길동",30 ) );

        System.out.println("총 객체 수 : " + set.size());
    }
}

위의 예제는 Member클래스를 생성하여 equals(), hashCode()를 오버라이딩 하였습니다.
equals()에서는 name과 age값이 같으면 true를 리턴되게, hashCode()에서는 name()과 age() 값이 같으면 동일한 hashCode()를
리턴되게 오버라이딩 해주었습니다.

Map 컬렉션

Map 컬렉션은 키와 값으로 구성된 Map.Entry 객체를 저장하는 구조를 가지고 있습니다. Entry는 Map인터페이스 내부에 선언된 중첩 인터페이스입니다. 키는 중복 저장될 수 없지만 값은 중복 저장될 수 있습니다. 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대체됩니다.

  • Map 컬렉션에서 공통적으로 사용 가능한 Map 인터페이스의 메소드들

* 키로 객체들을 관리하기 때문에 키를 매개값으로 갖는 메소드가 많습니다.

기능 메소드 설명
객체 추가 V put(K key, V value) 주어진 키로 값을 저장합니다. 새로운 키일 경우 null을 리턴하고 동일한 키가 있을 경우 값을 대체하고 이전 값을 리턴
객체 검색 boolean containsKey(Object Key) 주어진 키가 있는지 여부확인
boolean containsValue(Object value) 주어진 키가 있는지 여부확인
Set<Map.Entry<K,V>> entrySet() 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴
V get(Object key) 주어진 키가 있는 값을 리턴합니다.
boolean isEmpty() 컬렉션이 비어 있는지 여부 확인
Set<K> keySet() 모든 키를 Set 객체를 담아서 리턴합니다.
int size() 저장된 키의 총 수를 리턴합니다.
Collection<V> values() 저장된 모든 값을 Collection에 담아서 리턴합니다.
객체 삭제 void clear() 모든 Map.Entry(키와 값)를 삭제
V remove(Object key) 주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴

HashMap

HashMap은 Map 인터페이스를 구현한 대표적인 Map컬렉션입니다. HashMap의 키로 사용할 객체는 hashCode()와 equals()메소드를 오버라이딩해서 동등 객체가 될 조건을 정해야 합니다.

Map<String,Integer> map = new HashMap<String,Integer>();
Map<String,Integer> map = new HashMap<>();
// HashMap의 K와V타입 파라미터를 생략하면 왼쪽 Map에 지정된 타입을 따라 감

Hashtable

Hashtable은 HashMap과 동일한 내부 구조를 가지고 있습니다. Hashtable도 키로 사용할 객체는 hashCode()와 equals()메소드를 재정의해서 동등 객체가 될 조건을 정해야 합니다.
HahMap과의 차이점은 Hashtable은 동기화(synchronized) 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 Hashtable의 메소드를 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다는 것입니다. 그래서 멀티 스레드 환경에서 객체를 안전하게 추가,삭제할 수 있기 때문에 Hashtable은 스레드에 안전 합니다.

Map<String,Integer> map = new Hashtable<String,Integer>();
Map<String,Integer> map = new Hashtable<>();
// Hashtable의 K와V타입 파라미터를 생략하면 왼쪽 Map에 지정된 타입을 따라 감

LIFO와 FIFO 컬렉션

후입선출(LIFO: Last In First Out) : 나중에 넣은 객체가 먼저 빠져나가는 자료구조
선입선출(FIFO: First In First Out) : 먼저 넣은 객체가 먼저 빠져나가는 자료구조

Stack

stack 클래스 LIFO 자료구조를 구현한 클래스입니다. 다음 표는 Stack 클래스의 주요 메소드 입니다.

리턴 타입 메소드 설명
E push(E item) 주어진 객체를 스택에 넣음
E peek() 스택의 맨 위 객체를 가져옴.
객체를 스택에서 제거하지 않음
E pop() 스택의 맨 위 객체를 가져옴.
객체를 스택에서 제거함
Stack<E> stack = new Stack<>();

Queue

Queue 인터페이스는 FIFO 자료구조에서 사용되는 메소드를 정의하고 있습니다. 다음 표는 Queue 인터페이스에 정의되어 있는 메소드입니다.

리턴 타입 메소드 설명
boolean offer(E e) 주어진 객체를 넣음
E peek() 객체를 하나 가져옴. 객체를 큐에서 제거하지 않음
E poll() 객체를 하나 가져옴. 객체를 큐에서 제거함
Queue<E> queue = new LinkedList<>();

Queue 인터페이스를 구현한 대표적인 클래스는 LinkedList입니다. LinkedList는 List인터페이스를 구현했기 때문에 List컬렉션이기도 합니다.

직접 해보는 손코딩 (이름을 키로 점수를 값으로 저장하기)

package com.ch12.sec01.exam06;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class HashMapExample {

    public static void main ( String[] args ) {
        // HashMap의 E타입을 생략하면 왼쪽 타입을 보고 따라감 (생략가능)
        Map<String,Integer> map = new HashMap<>();

        map.put("신용권",85);
        map.put("홍길동",90);
        map.put("동장군",80);
        map.put("홍길동",95);
        // 겹치는 Key값이 존재(홍길동)하기 때문에 3개만 map에 저장됨
        System.out.println("총 Entry 수: " + map.size());
        // 객체 찾기
        System.out.println("\t 홍길동 : " + map.get( "홍길동" ));
        System.out.println();
        // 객체를 하나씩 처리
        Set<String> keySet = map.keySet();
        Iterator<String> iterator = keySet.iterator();
        while ( iterator.hasNext() ){
            String key = iterator.next();
            Integer value = map.get( key );
            System.out.println("\t" + key + " : " + value);
        }
        System.out.println();
        // 객체 삭제
        map.remove( "홍길동" );
        System.out.println("총 Entry 수 : " + map.size());

        Set<Map.Entry<String,Integer>> entrySet = map.entrySet(); // Map.Entry Set 얻기
        Iterator<Entry<String, Integer>> entryIterator = entrySet.iterator();

        while ( entryIterator.hasNext() ){
            Map.Entry<String,Integer> entry = entryIterator.next();
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println("\t" + key + " : " + value);
        }
        System.out.println();
        // 객체 전체 삭제
        map.clear();
        System.out.println("총 Entry 수 : " + map.size());
    }
}
결과값

반응형