Collection Framework

|

컬렉션 프레임워크란?

컬렉션 프레임워크(Collection Framework)

  • 컬렉션
    • 사전적 의미로 요소(객체)를 수집해서 저장하는 것
  • 배열의 문제점
    • 저장할 수 있는 객체 수가 배열을 생성할 때 결정
      • 불특정 다수의 객체를 저장하기에는 문제
    • 객체를 삭제했을 때 해당 인덱스가 비게됨
      • 낱알이 군데군데 빠진 옥수수가 될 수 있다.
      • 객체를 저장하려면 어디가 비어있는지 확인해야한다.
  • 컬렉션 프레임워크
    • 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 제공되는 컬렉션 라이브러리
    • java.util 패키지에 포함
    • 인터페이스를 통해서 정형화된 방법으로 다양한 컬렉션 클래스를 이용

컬렉션 프레임워크의 주요 인터페이스

  • List
    • 배열과 유사하게 인덱스로 관리
  • Set
    • 집합과 유사. 구슬 주머니에 객체를 저장하는 것과 같은 형태
  • Map
    • 키와 값의 쌍으로 관리
인터페이스 분류 분류 특징 구현 클래스
Collection List 계열 순서를 유지하고 저장, 중복 저장 가능 ArrayList, Vector, LinkedList
Collection Set 계열 순서를 유지하지 않고 저장, 중복 저장 불가 HashSet, TreeSet
  Map 계열 키와 값의 쌍으로 저장, 키는 중복 저장 안됨 HashMap, Hashtable, TreeMap, Properties

2. List 컬렉션

List 컬렉션의 특징 및 주요 메소드

  • 특징
    • 인덱스로 관리
    • 중복해서 객체 저장 가능
  • 구현 클래스
    • ArrayList
    • Vector
    • LinkedList
  • 주요 메소드
기능 메소드 설명
객체 추가 boolean add(E e) 주어진 객체를 맨끝에 추가
  void add(int index, E element) 주어진 인덱스에 객체를 추가
  set(int index, E element) 주어진 인덱스에 저장된 객체를 주어진 객체로 바꿈
객체 검색 boolean contains(Object o) 주어진 인덱스에 저장된 객체를 리턴
  E get(int index) 주어진 인덱스에 저장된 객체를 리턴
  isEmpty() 컬렉션이 비어 있는지 조사
  int size() 저장되어있는 전체 객체수를 리턴
객체 삭제 void clear() 주어진 인덱스에 저장된 객체를 삭제
  E remove(int index) 주어진 인덱스에 저장된 객체를 삭제 후 리턴
  boolean remove(Object o) 주어진 객체를 삭제
  • 전체 객체를 대상으로 반복해서 얻기
    • 저장된 총 객체수 만큼 루핑
List<String> list = new ArrayList<>;
for(int i = 0; i < list.size(); i++) {
  String str = list.get(i);
}

또는

for(String str : list) {
  ...
}

ArrayList

  • 저장 용량(capacity)

    • 초기 : 10
    • 초기 용량 지정 가능
    • 저장 용량을 초과한 객체들이 들어오면 자동적으로 늘어난다.
  • 객체 제거

    • 바로 뒤 인덱스부터 마지막 인덱스까지 모두 앞으로 1씩 당겨진다.
  • 고정된 객체들로 구성된 List 생성

    • List<T> list = Arrays.asList(T... a);
    • 자동언박싱
    List<String> list1 = Arrays.asList("홍길동", "김남준", "김자바");
    for(String name : list1) {
      System.out.println(name);
    }
    
    List<Integer> list2 = Arrays.asList(1, 2, 3);
    for(int value : list2) {
      System.out.println(value);
    }
    
  • 예제

package list;

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

public class ArrayListEx {
  public static void main(String[] args) {
    List<String> list = new ArrayList<>();

    list.add("Java");
    list.add("JDBC");
    list.add("Servlet/JSP");
    list.add(2, "Database");
    list.add("Mybatis");
    System.out.println(list.size());
    System.out.println(list);

    String skill = list.get(2);
    System.out.println("2: " + skill);

    list.forEach(str -> System.out.println(list.indexOf(str) + ":" + str + " "));
  }
}
5
[Java, JDBC, Database, Servlet/JSP, Mybatis]
2: Database
0:Java 
1:JDBC 
2:Database 
3:Servlet/JSP 
4:Mybatis 

Process finished with exit code 0

Vector

List<E> list = new Vector<>();
  • 특징
    • Vector는 스레드 동기화(synchronization)가 되어 있기 때문에 복수의 스레드가 동시에 Vector에 접근해서 객체를 추가, 삭제 하더라도 스레드에 안전 하다.(Thread Safe 하다.)
    • 그렇기 때문에 멀티스레드 환경에서 index로 데이터를 관리하고 싶은 경우 Vector를 사용하는 것이 안전하다.
    • 반면에, 싱글스레드 환경에서는 ArrayList를 사용하는 것이 더 빠르다. synchronized처리가 되어있지 않기 때문이다.
  • 예제
package list.ex2_vector;

public class Board {
  private String subject;
  private String content;
  private String writer;

  public Board(String subject, String content, String writer) {
    this.subject = subject;
    this.content = content;
    this.writer = writer;
  }

  @Override
  public String toString() {
    return subject + " " + content + " " + writer;
  }
}
package list.ex2_vector;

import java.util.List;
import java.util.Vector;

public class VectorEx {
  public static void main(String[] args) {
    List<Board> list = new Vector<>();

    list.add(new Board("제목1", "내용1", "작성자1"));
    list.add(new Board("제목2", "내용2", "작성자2"));
    list.add(new Board("제목3", "내용3", "작성자3"));
    list.add(new Board("제목4", "내용4", "작성자4"));
    list.add(new Board("제목5", "내용5", "작성자5"));

    list.remove(2);
    list.remove(3);

    list.forEach(board -> System.out.println(board));
  }
}

제목1 내용1 작성자1
제목2 내용2 작성자2
제목4 내용4 작성자4

LinkedList

List<E> list = new LinkedList<>();
  • 특징
    • 인접 참조를 링크해서 체인처럼 관리
    • 특정 인덱스에서 객체를 제거하거나 추가하게 되면 바로 앞뒤 링크만 변경
    • 빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList보다 좋은 성능을 발휘(특정 인덱스일 경우 훨씬 더)
  • 예제
package list.ex3_linkedlist;

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

public class ArrayLinked {
  public static void main(String[] args) {
    List<String> arrayList = new ArrayList<>();
    List<String> linkedList = new LinkedList<>();

    long startTime;
    long endTime;

    startTime = System.nanoTime();
    for (int i = 0; i < 10000; i++) {
      arrayList.add(0, String.valueOf(i));
    }
    endTime = System.nanoTime();
    System.out.println("arrayList 걸린 시간 : " + (endTime - startTime) + "ns");

    startTime = System.nanoTime();
    for (int i = 0; i < 10000; i++) {
      linkedList.add(0, String.valueOf(i));
    }
    endTime = System.nanoTime();
    System.out.println("linkedList 걸린 시간 : " + (endTime - startTime) + "ns");
  }
}
arrayList 걸린 시간 : 17661954ns
linkedList 걸린 시간 : 4967602ns

Process finished with exit code 0

3. Set 컬렉션

Set 컬렉션의 특징 및 주요 메소드

  • 특징
    • 수학의 집합에 비유될 수 있다.
    • 저장 순서가 유지되지 않는다.
    • 객체를 중복해서 저장할 수 없고,
    • 하나의 null만 저장할 수 있다.
  • 구현 클래스
    • HashSet, LinkedHashSet, TreeSet
  • 주요 메소드
기능 메소드 설명
객체 추가 boolean add(E e) 주어진 객체를 저장, 객체가 성공적으로 저장되면 true를 리턴하고 중복 객체면 false를 리턴
객체 검색 boolean contains(Object o) 주어진 객체가 저장되어 있는지 여부
  isEmpty() 컬렉션이 비어 있는지 조사
  Iterator<E> iterator() 저장된 객체를 한번씩 가져오는 반복자 리턴
  int size() 저장되어있는 전체 객체 수 리턴
객체 삭제 void clear() 저장된 모든 객체를 삭제
  boolean remove(Object o) 주어진 객체를 삭제
  • 객체 추가 및 삭제
Set<String> set = ...;
set.add("홍길동");
set.remove("홍길동");
  • Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드가 없다.
  • 대신, 전체 객체를 대상으로 한번씩 반복해서 가져오는 반복자(Iterator)를 제공한다.
Set<String> set = ...;
Iterator<String> iterator = set.iterator();
리턴타입 메소드명 설명
boolean hasNext() 가져올 객체가 있으면 true를 반환하고 없으면 false를 반환한다.
E next() 컬렉션에서 하나의 객체를 가져온다.
void remove() Set 컬렉션에서 객체를 제거한다.
Set<String> set = ...;
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
  String str = iterator.next();
}

or

for(String str : set) {
  ...
}
  • 반복자를 통한 객체 제거
    • iterator의 메소드이지만 set컬렉션의 객체가 제거된다.
while(iterator.hasNext()) {
  String str = iterator.next();
  if(str.equals("홍길동")) {
    iterator.remove();
  }
}

HashSet

Set<E> set = new HashSet<E>();
  • 특징

    • 동일 객체 및 동등 객체는 중복 저장하지 않는다.
    • 동등 객체 판단 방법
      • 동등객체가 되려면?
        • 두 객체의 hashCode()의 리턴값이 같을 경우, 다시 두 객체의 equals()의 리턴값을 비교하여 같을 경우 동등 객체로 판단한다.
        • hashCode() 리턴값이 다르거나, hashCode() 리턴값은 같지만 equals()의 리턴값이 다른 경우 다른 객체로 판단한다.
  • 예제

    • HashSet
    package set;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    
    public class HashSetEx {
      public static void main(String[] args) {
        Set<String> set = new HashSet<>();
    
        set.add("java");
        set.add("JDBC");
        set.add("java");
        set.add("Servlet/JSP");
        set.add("mybatis");
    
        System.out.println(set.size());
    
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
          System.out.print(iterator.next() + " ");
        }
    
        System.out.println();
        for (String str : set) {
          System.out.print(str + " ");
        }
    
        System.out.println();
        set.clear();
        System.out.println(set);
      }
    }
    
    4
    java mybatis JDBC Servlet/JSP 
    java mybatis JDBC Servlet/JSP 
    []
    
    Process finished with exit code 0
    
    • hashCode(), equals() Override
    package set.hashcode_equals;
    
    import java.util.Objects;
    
    public class Member {
      public String name;
      public int age;
    
      public Member(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public boolean equals(Object object) {
        if(object instanceof Member) {
          Member member = (Member) object;
          return member.name.equals(name) && member.age == age;
        } else {
          return false;
        }
      }
    
      @Override
      public int hashCode() {
        return Objects.hash(name, age);
      }
    }
    
    package set.hashcode_equals;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class HashSetEx2 {
      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());
      }
    }
    
    1
    
    Process finished with exit code 0
    

4. Map 컬렉션

Map 컬렉션의 특징 및 주요 메소드

  • 특징
    • 키(Key)와 값(value)으로 구성된 Map.Entry 객체를 저장하는 구조
    • 키와 값은 모두 객체
    • 키는 중복될 수 없지만 값은 중복 저장 가능
  • 구현 클래스
    • HashMap, Hashtable, LinkedHashMap, Properties, TreeMap
  • 주요 메소드
기능 메소드 설명
객체 추가 V put(K key, V value) 주어진 키와 값을 추가, 저장이 되면 값을 리턴
객체 검색 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 삭제, 삭제가 되면 값을 리턴
  • 객체 추가, 찾기, 삭제
Map<String, Integer> map = ...;
map.put("홍길동", 30);
int score = map.get("홍길동");
map.remove("홍길동")
  • 전체 객체를 대상으로 반복해서 얻기
    • Key-Value 쌍을 반복적으로 다루기 위해서 Set인터페이스 형태로 keySet과 entrySet을 저장하고 iterator를 이용해 반복 접근한다.
Map<K,V> map = ...;
Set<K> keySet = map.keySet();
Iterator<K> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
  K key = keyIterator.next();
  V value = map.get(key);
}
Set<Map.Entry<K,V>> entrySet = map.entrySet();
Iterator<Map.Entry<K,V>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
  Map.Entry<K,V> entry = entryIterator.next();
  K key = entry.getKey();
  V value = entry.getValue();
}

HashMap

Map<K, V> map = new HashMap<K, V>();
  • 특징

    • 키 객체는 hashCode()와 equals()를 재정의해서 동등객체가 될 조건을 정해야 한다.
      • 동등객체가 되려면?
        • 두 객체의 hashCode()의 리턴값이 같을 경우, 다시 두 객체의 equals()의 리턴값을 비교하여 같을 경우 동등 객체로 판단한다.
        • hashCode() 리턴값이 다르거나, hashCode() 리턴값은 같지만 equals()의 리턴값이 다른 경우 다른 객체로 판단한다.
    • 키 타입은 String을 많이 사용
      • String은 문자열이 같을 경우 동등 객체가 될 수 있도록 hashCode()와 equals()메소드가 재정의되어 있기 때문
  • 예제

    • HaspMap Iterator 사용
    package map.hashmap;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    public class HaspMapEx1 {
      public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
    
        map.put("신용권", 40);
        map.put("홍길동", 30);
        map.put("김남준", 32);
        map.put("홍길동", 32);
        System.out.println(map.size());
        System.out.println(map);
    
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
    
        System.out.println("##entrySet()");
        while(entryIterator.hasNext()) {
          Map.Entry<String, Integer> entry = entryIterator.next();
          System.out.print(entry.getKey() + " ");
          System.out.println(entry.getValue());
        }
    
        Set<String> keySet = map.keySet();
        Iterator<String> keyIterator = keySet.iterator();
    
        System.out.println("##keySet()");
        while(keyIterator.hasNext()) {
          String key = keyIterator.next();
          int value = map.get(key);
          System.out.println(key + " " + value);
        }
    
        map.clear();
        System.out.println(map.size());
      }
    }
    
    3
    {홍길동=32, 신용권=40, 김남준=32}
    ##entrySet()
    홍길동 32
    신용권 40
    김남준 32
    ##keySet()
    홍길동 32
    신용권 40
    김남준 32
    0
    
    Process finished with exit code 0
    
    • 중복 키값 저장 안함, 가장 최근 추가된 value로 대체
    package map.hashcode_equals;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class HashMapEx2 {
      public static void main(String[] args) {
        Map<Student, Integer> map = new HashMap<>();
    
        map.put(new Student(1, "홍길동"), 95);
        map.put(new Student(1, "홍길동"), 97);
    
        System.out.println("총 Entry 수 : " + map.size());
        System.out.println(map.get(new Student(1, "홍길동")));
      }
    }
    
     Entry  : 1
    97
    
    Process finished with exit code 0
    

Hashtable

Map<K, V> map = new Hashtable<>();
  • 특징

    • 키 객체는 hashCode()와 equals()를 재정의해서 동등객체가 될 조건을 정해야 한다.
      • 동등객체가 되려면?
        • 두 객체의 hashCode()의 리턴값이 같을 경우, 다시 두 객체의 equals()의 리턴값을 비교하여 같을 경우 동등 객체로 판단한다.
        • hashCode() 리턴값이 다르거나, hashCode() 리턴값은 같지만 equals()의 리턴값이 다른 경우 다른 객체로 판단한다.
    • Hashtable은 스레드 동기화(synchronization)가 되어 있기 때문에 복소의 스레드가 동시에 Hashtable에 접근해서 객체를 추가, 삭제하더라고 스레드레 안전(thread safe)하다.
      • ArrayList와 Vector의 관계와 같다.
      • 멀티스레드 환경에서는 Hashtable을 사용하는 것이 안전하며, 싱글스레드 환경에서는 동기화처리가 되지 않은 HashMap을 사용하는 것이 성능에 더 좋다.
  • 예제

    • 기본 사용법은 HashMap이랑 같다. Thread Safe여부만 다르다.
    package map.hashtable;
    
    import java.util.Hashtable;
    import java.util.Map;
    import java.util.Scanner;
    
    public class HashtableEx {
      public static void main(String[] args) {
        Map<String, String> map = new Hashtable<>();
    
        map.put("spring", "12");
        map.put("summer", "123");
        map.put("fall", "1234");
        map.put("winter","12345");
    
        Scanner scanner = new Scanner(System.in);
        while(true) {
          System.out.println("아이디와 비밀번호를 입력해주세요.");
          System.out.print("아이디 :");
          String id = scanner.nextLine();
          System.out.print("비밀번호 :");
          String pw = scanner.nextLine();
          System.out.println();
          if(map.containsKey(id)) {
            if(map.get(id).equals(pw)) {
              System.out.println("로그인 되었습니다.");
            } else {
              System.out.println("비밀번호가 맞지 않습니다.");
            }
          } else {
            System.out.println("아이디가 존재하지 않습니다.");
          }
        }
      }
    }
    
    아이디와 비밀번호를 입력해주세요.
    아이디 :spring
    비밀번호 :12
    
    로그인 되었습니다.
    아이디와 비밀번호를 입력해주세요.
    아이디 :spring
    비밀번호 :123
    
    비밀번호가 맞지 않습니다.
    

Properties

  • 특징

    • 키와 값을 String 타입으로 제한한 Map 컬렉션이다
    • Properties는 프로퍼티(~.properties) 파일을 읽어 들일 때 주로 사용한다.
  • 프로퍼티 파일

    • 옵션 정보, 데이터베이스 연결정보, 국제화(다국어) 정보를 기록한 텍스트 파일로 활용
    • 애플리케이션에서 주로 변경이 잦은 문자열을 저장해서 유지 보수를 편리하게 만들어줌
    driver=oracle.jdbc.OracleDriver
    url=jdbc:oravle:thin@localhost:1521:orcl
    username=scott
    password=tiger
    
    • 키와 값이 =기호로 연결되어 있는 텍스트파일로 ISO 8859-1 문자셋으로 저장
    • 한글은 유니코드로 변환되어 저장
  • Properties 객체 생성

    • 파일 시스템 경로 이용
    Properties properties = new Properties();
    properties.load(new FileReader("C:/~/database.properties"));
    
    • ClassPath를 이용
    String path = 클래스.class.getResource("database.properties").getPath();
    path = URLDecoder.decode(path, "utf-8");	//경로에 한글이 있을 경우 한글 복원
    Properties properties = new Properties();
    properties.load(new FileReader(path));
    
    //패키지가 다를경우
    String path = A.class.getResource("config/database.properties").getPath();
    
  • 값 읽기

String value = properties.getProperty("key");
  • 예제
package map.properties;

import java.io.FileReader;
import java.net.URLDecoder;
import java.util.Properties;

public class PropertiesEx {
  public static void main(String[] args) throws Exception {
    Properties properties = new Properties();
    String path = PropertiesEx.class.getResource("database.properties").getPath();
    path = URLDecoder.decode(path, "utf-8");
    properties.load(new FileReader(path));

    String driver = properties.getProperty("driver");
    String url = properties.getProperty("url");

    System.out.println(driver);
    System.out.println(url);
  }
}
driver=oracle.jdbc.OracleDriver
url=jdbc:oravle:thin@localhost:1521:orcl
username=scott
password=tiger
oracle.jdbc.OracleDriver
jdbc:oravle:thin@localhost:1521:orcl

Process finished with exit code 0

5. 검색 기능을 강화시킨 컬렉션

검색기능을 강화시킨 컬렉션

  • TreeSet, TreeMap
    • 이진트리(binary tree)를 사용하기 때문에 검색 속도 향상

이진 트리 구조

  • 부모 노드와 자식 노드로 구성
    • 왼쪽 자식 노드 : 부모 보다 작은 값
    • 오른쪽 자식 노드 : 부모 보다 큰 값
  • 정렬이 쉬움
    • 오름 차순 : 왼쪽노드 -> 부모노드 -> 오른쪽노드
    • 내림 차순 : 오른쪽노드 -> 부모노드 -> 왼쪽노드
  • 범위 검색이 쉽다
    • 노드를 기준으로 왼쪽 자식트리(작은 값들)와 오른쪽 자식트리(큰 값들)의 속성이 결정되어있음

TreeSet

TreeSet<E> treeSet = new TreeSet<>();

TreeSet은 Set 인터페이스의 구현 클래스이므로 Set인터페이스의 타입의 변수에 대입해도 되지만, TreeSet 타입의 변수에 대입한 이유는 TreeSet이 가지고 있는 검색 기능 메소드를 사용하기 위함이다.

  • 특징

    • 이진 트리(Binary Tree)를 기반으로 한 Set 컬렉션
    • 왼쪽과 오른쪽 자식노드를 참조하기 위한 두개의 변수로 구성
  • 주요 메소드

    • 특정 객체를 찾는 메소드

      • first(), last(), lower(), higher(), …
      package advanced_search_collection.treeset;
      
      import java.util.Iterator;
      import java.util.TreeSet;
      
      public class TreeSetEx01 {
        public static void main(String[] args) {
          TreeSet<Integer> scores = new TreeSet<>();
      
          scores.add(87);
          scores.add(98);
          scores.add(75);
          scores.add(95);
          scores.add(80);
      
          System.out.println("최소값(first): " + scores.first());
          System.out.println("최대값(last): " + scores.last());
          System.out.println("95 아래에 있는 값(미만, lower): " + scores.lower(95));
          System.out.println("95 위에 있는 값(초과, higher): " + scores.higher(95));
          System.out.println("95이거나 바로 아래 있는 값(이하, floor): " + scores.floor(95));
          System.out.println("95이거나 바로 위에 있는 값(이상, ceiling): " + scores.ceiling(95));
          System.out.println();
      
          System.out.println("Iterator");
          Iterator<Integer> iterator = scores.iterator();
          while(iterator.hasNext()) {
            System.out.println(iterator.next());
          }
          System.out.println();
      
          System.out.println("최소값(pollFirst)/최대값(pollLast) 추출 후 set에서 제거");
          while (!scores.isEmpty()) {
            int score = scores.pollFirst();
      //      int score = scores.pollLast();
            System.out.print("빼낸 값: " + score);
            System.out.println(" / 남은 객체 수: " + scores.size());
          }
          System.out.println();
        }
      }
      
      최소값(first): 75
      최대값(last): 98
      95 아래에 있는 (미만, lower): 87
      95 위에 있는 (초과, higher): 98
      95이거나 바로 아래 있는 (이하, floor): 95
      95이거나 바로 위에 있는 (이상, ceiling): 95
      
      Iterator
      75
      80
      87
      95
      98
      
      최소값(pollFirst)/최대값(pollLast) 추출  set에서 제거
      빼낸 : 75 / 남은 객체 : 4
      빼낸 : 80 / 남은 객체 : 3
      빼낸 : 87 / 남은 객체 : 2
      빼낸 : 95 / 남은 객체 : 1
      빼낸 : 98 / 남은 객체 : 0
      

      Process finished with exit code 0 ​```

    • 정렬 메소드

      • descendingIterator(), descendingSet()
      package advanced_search_collection.treeset;
      
      import java.util.NavigableSet;
      import java.util.TreeSet;
      
      public class TreeSetEx02 {
        public static void main(String[] args) {
          TreeSet<Integer> scores = new TreeSet<>();
      
          scores.add(87);
          scores.add(98);
          scores.add(75);
          scores.add(95);
          scores.add(80);
          System.out.println(scores);
      
          NavigableSet<Integer> descendingSet = scores.descendingSet();
          System.out.println("descendingSet");
          for(int score : descendingSet) {
            System.out.print(score + " ");
          }
      
          System.out.println();
          System.out.println("ascendingSet");
          NavigableSet<Integer> ascendSet = descendingSet.descendingSet();
          for(int score : ascendSet) {
            System.out.print(score + " ");
          }
        }
      }
      
      [75, 80, 87, 95, 98]
      descendingSet
      98 95 87 80 75 
      ascendingSet
      75 80 87 95 98 
      Process finished with exit code 0
      
    • 범위 검색 메소드

      • headSet(), tailSet, subSet()
      package advanced_search_collection.treeset;
      
      import java.util.NavigableSet;
      import java.util.TreeSet;
      
      public class TreeSetEx03 {
        public static void main(String[] args) {
          TreeSet<String> treeSet = new TreeSet<>();
      
          treeSet.add("apple");
          treeSet.add("forever");
          treeSet.add("description");
          treeSet.add("ever");
          treeSet.add("zoo");
          treeSet.add("base");
          treeSet.add("guess");
          treeSet.add("cherry");
          treeSet.add("f");
          treeSet.add("c");
      
          System.out.println("[c~f사이의 단어 검색]");
          NavigableSet<String> rangeSet = treeSet.subSet("c", true, "f", true);
          System.out.println(rangeSet);
        }
      }
      
      [c~f사이의 단어 검색]
      [c, cherry, description, ever, f]
      
      Process finished with exit code 0
      

TreeMap

TreeMap<K, V> treeMap = new TreeMap<K, V>();

검색속도를 향상 시키기 위해서 제공되는 컬렉션, 제공되는 객체를 MapEntry형태로 제공하는 컬렉션

  • 특징

    • 이진 트리(Binary Tree)를 기반으로한 Map 컬렉션
    • 키와 값이 저장된 Map.Entry를 저장
    • 왼쪽과 오른쪽 자식노드를 참조하기 위한 두개의 변수로 구성
  • 주요 메소드

    • 단일 노드 객체를 찾는 메소드

      • firstEntry(), lastEntry(), lowerEntry(), lowerEntry(), higherEntry(), …
      package advanced_search_collection.treemap;
      
      import java.util.Map;
      import java.util.TreeMap;
      
      public class TreeMapEx01 {
        public static void main(String[] args) {
          TreeMap<Integer, String> scores = new TreeMap<>();
          scores.put(87, "홍길동");
          scores.put(98, "이동수");
          scores.put(75, "박길순");
          scores.put(95, "신용권");
          scores.put(80, "김자바");
      
          Map.Entry<Integer, String> entry = scores.firstEntry();
          System.out.println("가장 작은 키값을 가진 객체: " + entry.getKey() + ", " + entry.getValue());
      
          entry = scores.lastEntry();
          System.out.println("가장 큰 키값을 가진 객체: " + entry.getKey() + ", " + entry.getValue());
      
          entry = scores.lowerEntry(95);
          System.out.println("95점 미만의 키값을 가진 객체: " + entry.getKey() + ", " + entry.getValue());
      
          entry = scores.higherEntry(95);
          System.out.println("95점 초과의 키값을 가진 객체: " + entry.getKey() + ", " + entry.getValue());
      
          entry = scores.floorEntry(95);
          System.out.println("95점 이상의 키값을 가진 객체: " + entry.getKey() + ", " + entry.getValue());
      
          entry = scores.ceilingEntry(95);
          System.out.println("95점 이하의 키값을 가진 객체: " + entry.getKey() + ", " + entry.getValue());
      
          System.out.println("키의 값이 최소값(pollFirst)/최대값(pollLast)인 객체 추출 후 Map에서 제거");
          while (!scores.isEmpty()) {
            //entry = scores.pollFirstEntry();
            entry = scores.pollLastEntry();
            System.out.println(entry.getKey() + ", " + entry.getValue() + "(남은 객체 수: " + scores.size() + ")");
          }
        }
      }
      
      가장 작은 키값을 가진 객체: 75, 박길순
      가장  키값을 가진 객체: 98, 이동수
      95 미만의 키값을 가진 객체: 87, 홍길동
      95 초과의 키값을 가진 객체: 98, 이동수
      95 이상의 키값을 가진 객체: 95, 신용권
      95 이하의 키값을 가진 객체: 95, 신용권
      
      키의 값이 최소값(pollFirst)/최대값(pollLast) 객체 추출  Map에서 제거
      98, 이동수(남은 객체 : 4)
      95, 신용권(남은 객체 : 3)
      87, 홍길동(남은 객체 : 2)
      80, 김자바(남은 객체 : 1)
      75, 박길순(남은 객체 : 0)
      
      Process finished with exit code 0
      
    • 정렬 메소드

      • descendingKetSet(), descendingMap()
      package advanced_search_collection.treemap;
      
      import java.util.Map;
      import java.util.NavigableMap;
      import java.util.Set;
      import java.util.TreeMap;
      
      public class TreeMapEx02 {
        public static void main(String[] args) {
          TreeMap<Integer, String> scores = new TreeMap<>();
          scores.put(87, "홍길동");
          scores.put(98, "이동수");
          scores.put(75, "박길순");
          scores.put(95, "신용권");
          scores.put(80, "김자바");
          System.out.println(scores);
      
          NavigableMap<Integer, String> descendingMap = scores.descendingMap();
          Set<Map.Entry<Integer, String>> descendingEntrySet = descendingMap.entrySet();
          for(Map.Entry<Integer, String> entry : descendingEntrySet) {
            System.out.print(entry.getKey() + "-" + entry.getValue() + " ");
          }
          System.out.println();
      
          NavigableMap<Integer, String> ascendingMap = descendingMap.descendingMap();
          Set<Map.Entry<Integer, String>> ascendingEntrySet = ascendingMap.entrySet();
          for(Map.Entry<Integer, String> entry : ascendingEntrySet) {
            System.out.print(entry.getKey() + "-" + entry.getValue() + " ");
          }
          System.out.println();
        }
      }
      
      {75=박길순, 80=김자바, 87=홍길동, 95=신용권, 98=이동수}
      98-이동수 95-신용권 87-홍길동 80-김자바 75-박길순 
      75-박길순 80-김자바 87-홍길동 95-신용권 98-이동수 
      
      Process finished with exit code 0
      
    • 범위 검색 메소드

      • headMap(), tailMap(), subMap()
      package advanced_search_collection.treemap;
      
      import java.util.Map;
      import java.util.NavigableMap;
      import java.util.TreeMap;
      
      public class TreeMapEx03 {
        public static void main(String[] args) {
          TreeMap<String, Integer> treeMap = new TreeMap<>();
          treeMap.put("apple", 10);
          treeMap.put("forever", 60);
          treeMap.put("description", 40);
          treeMap.put("ever", 50);
          treeMap.put("zoo", 10);
          treeMap.put("base", 20);
          treeMap.put("guess", 70);
          treeMap.put("cherry", 30);
          treeMap.put("f", 5);
          treeMap.put("c", 80);
      
          System.out.println("[c~f 사이의 단어 검색]");
          NavigableMap<String, Integer> rangeMap = treeMap.subMap("c", true, "f", true);
          for (Map.Entry<String, Integer> entry : rangeMap.entrySet()) {
            System.out.print(entry.getKey() + "-" + entry.getValue() + " ");
          }
        }
      }
      
      [c~f 사이의 단어 검색]
      c-80 cherry-30 description-40 ever-50 f-5 
      Process finished with exit code 0
      

Comparable과 Comparator

  • TreeSet과 TreeMap의 자동 정렬

    • TreeSet의 객체와 TreeMap의 키는 저장과 동시에 자동 오름차순으로 정렬
    • 숫자(Integer, Double)타입일 경우에는 값으로 정렬
    • 문자열(String)타입일 경우에는 값으로 정렬
    • TreeSet과 TreeMap은 정렬을 위해 java.lang.Comparable을 구현한 객체를 요구
      • Integer, Double, String은 모두 Comparable 인터페이스를 구현한 클래스이기 때문에 TreeSet과 TreeMap에 저장할 때, 자동으로 오름차순으로 정렬될 수 있다.
      • 사용자가 정의하는 객체를 TreeSet, TreeMap에 저장하려고 할 때, Comparable을 구현하고 있지 않을 경우에는 저장하는 순간 ClassCastException이 발생
  • 사용자 정의 객체를 정렬하고 싶을 경우

    • 방법1 : 사용자 정의 클래스가 Comparable을 구현
    리턴타입 메소드 설명
    int compareTo(T o) 주어진 객체과 같으면 0을 리턴, 주어진 객체보자 적으면 음수를 리턴, 주어진 객체보다 크면 양수를 리턴
    package comparable;
    
    public class Person implements Comparable<Person> {
      private String name;
      private int age;
    
      public Person(String name, int age) {
        this.name = name;
        this.age = age;
      }
    
      @Override
      public int compareTo(Person o) {
        if (age < o.getAge()) return -1;
        else if (age == o.age) return 0;
        else return 1;
      }
    
      public String getName() {
        return name;
      }
    
      public int getAge() {
        return age;
      }
    
      @Override
      public String toString() {
        return age + "-" + name;
      }
    }
    
    package comparable;
    
    import java.util.Iterator;
    import java.util.TreeSet;
    
    public class ComparableEx {
      public static void main(String[] args) {
        TreeSet<Person> treeSet = new TreeSet<>();
        treeSet.add(new Person("김남준", 25));
        treeSet.add(new Person("신용권", 40));
        treeSet.add(new Person("김자바", 30));
        System.out.println(treeSet);
    
        Iterator<Person> iterator = treeSet.iterator();
        while(iterator.hasNext()) {
          Person person = iterator.next();
          System.out.println(person);
        }
      }
    }
    
    [25-김남준, 30-김자바, 40-신용권]
    25-김남준
    30-김자바
    40-신용권
    
    Process finished with exit code 0
    
    • 방법2 : TreeSet, TreeMap 생성시 Comparator 구현 객체 제공
    리턴타입 메소드 설명
    int compare(T o1, T o2) o1과 o2가 동등하다면 0을 리턴, o1이 o2보다 앞에 오게 하려면 음수를 리턴, o1과 o2보다 뒤에 오게 하려면 양수를 리턴
    package comparator;
    
    public class Fruit {
      private String name;
      private int price;
    
      public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
      }
    
      public String getName() {
        return name;
      }
    
      public int getPrice() {
        return price;
      }
    
      @Override
      public String toString() {
        return price + "-" + name;
      }
    }
    
    package comparator;
    
    import java.util.Comparator;
    
    public class DescendingComparator implements Comparator<Fruit> {
    
      @Override
      public int compare(Fruit o1, Fruit o2) {
        if (o1.getPrice() < o2.getPrice()) return -1;
        else if (o1.getPrice() == o2.getPrice()) return 0;
        else return 1;
      }
    }
    
    package comparator;
    
    import java.util.Iterator;
    import java.util.TreeSet;
    
    public class ComparatorEx {
      public static void main(String[] args) {
        TreeSet<Fruit> treeSet = new TreeSet<>(new DescendingComparator());
    
        treeSet.add(new Fruit("포도", 3000));
        treeSet.add(new Fruit("수박", 10000));
        treeSet.add(new Fruit("딸기", 6000));
        System.out.println(treeSet);
    
        Iterator<Fruit> iterator = treeSet.iterator();
        while(iterator.hasNext()) {
          Fruit fruit = iterator.next();
          System.out.println(fruit);
        }
      }
    }
    
    [3000-포도, 6000-딸기, 10000-수박]
    3000-포도
    6000-딸기
    10000-수박
    
    Process finished with exit code 0
    

6. LIFO와 FIFO 컬렉션

Last In First Out, First In First Out

Stack 클래스

Stack<E> stack = new Stack<>();
  • 특징
    • 후입선출(Last In First Out) 구조
    • 응용 예: JVM 스택 메모리
  • 주요 메소드

    리턴타입 메소드 설명
    E push(E item) 주어진 객체를 스택에 넣는다
    E peek() 스택의 맨위 객체를 가져온다. 객체를 스택에서 제거하지 않는다.
    E pop() 스택의 맨위 객체를 가져온다. 객체를 스택에서 제거한다.
  • 예제 코드
package stack;

public class Coin {
  private int value;

  public Coin(int value) {
    this.value = value;
  }

  public int getValue(){
    return value;
  }
}
package stack;

import java.util.Stack;

public class StackEx {
  public static void main(String[] args) {
    Stack<Coin> coinBox = new Stack<>();

    coinBox.push(new Coin(100));
    coinBox.push(new Coin(500));
    coinBox.push(new Coin(50));

    while(!coinBox.isEmpty()) {
      System.out.println("꺼낸 동전 : " + coinBox.pop().getValue() + "원");
    }
  }
}
꺼낸 동전 : 50
꺼낸 동전 : 500
꺼낸 동전 : 100

Process finished with exit code 0

Queue 클래스

Queue queue = new LinkedList();
  • 특징

    • 선입선출(First In First Out) tnwh
    • 응용 예: 작업큐, 메세지큐, …
    • 구현 클래스 : LinkedList
  • 주요 메소드

    리턴타입 메소드 설명
    boolean offer(E e) 주어진 객체를 넣는다.
    E peek() 객체 하나를 가져온다. 객체를 큐에서 제거하지 않는다.
    E poll() 객체 하나를 가져온다. 객체를 큐에서 제거한다.
  • 예제 코드

package queue;

public class Message {
  private String command;
  private String to;

  public Message(String command, String to) {
    this.command = command;
    this.to = to;
  }

  public String getCommand() {
    return command;
  }

  public String getTo() {
    return to;
  }
}
package queue;

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

public class QueueEx {
  public static void main(String[] args) {
    Queue<Message> messageQueue = new LinkedList<>();

    messageQueue.offer(new Message("send Email", "홍길동"));
    messageQueue.offer(new Message("send SMS", "신용권"));
    messageQueue.offer(new Message("send Kakaotalk", "김남준"));

    while(!messageQueue.isEmpty()) {
      Message message = messageQueue.poll();
      switch(message.getCommand()) {
        case "send Email":
          System.out.println(message.getTo() + "님에게 메일을 보냅니다.");
          break;
        case "send SMS":
          System.out.println(message.getTo() + "님에게 SMS를 보냅니다.");
          break;
        case "send Kakaotalk":
          System.out.println(message.getTo() + "님에게 KakaoTalk 보냅니다.");
          break;
      }
    }
  }
}
홍길동님에게 메일을 보냅니다.
신용권님에게 SMS 보냅니다.
김남준님에게 KakaoTalk 보냅니다.

Process finished with exit code 0

7. 동기화된(synchronized) 컬렉션

비동기화된 컬렉션을 동기화된 컬렉션으로 래핑

비동기화된 컬렉션을 멀티스레드 환경에서 사용해야 할 경우 동기화된 컬렉션으로 래핑하여 사용하는 것이 안전하다.

List 컬렉션

List<T> list = Collections.synchronizedList(new ArrayList<T>());
  • 위와 같이 비동기화된 컬렉션인 ArrayList를 Collections클래스의 정적메소드를 통해서 래핑시켜주면 Thread Safe한 컬렉션을 만들 수 있다.

Set 컬렉션

Set<E> set = Collections.synchronizedSet(new HashSet<E>);
  • 마찬가지로 비동기화된 컬렉션인 HashSet를 Collections클래스의 정적메소드를 통해서 래핑시켜주면 Thread Safe하게 만들 수 있다.

Map 컬렉션

Map<K, V> map = Collections.synchronizedMap(new HashMap<K, V>));
  • 마찬가지로 비동기화된 컬렉션인 HashMap를 Collections클래스의 정적메소드를 통해서 래핑시켜주면 Thread Safe하게 만들 수 있다.

Comments