[Java] 컬렉션 프레임워크 ⑤

kiteB·2022년 2월 25일
0

Java2

목록 보기
14/36
post-thumbnail

1. TreeMap

TreeMap은 이진 트리를 기반으로 한 Map 컬렉션이다.

✅ TreeMap

  • TreeSet과는 달리 키와 값이 저장된 Map.Entry를 저장한다.
  • TreeMap에 객체를 저장하면 자동으로 정렬되는데, 기본적으로 부모 키값과 비교해서
    키 값이 낮은 것은 왼쪽 자식 노드에, 키 값이 높은 것은 오른쪽 자식 노드에 Map.Entry 객체를 저장한다.

✅ TreeMap 생성 방법

TreeMap을 생성하기 위해서는 키로 저장할 객체 타입과 값으로 저장할 객체 타입을 타입 파라미터로 주고 기본 생성자를 호출하면 된다.

TreeMap<K, V> treeMap = new TreeMap<K, V>();
  • 키로 String 타입을 사용하고 값으로 Integer 타입을 사용하는 TreeMap은 다음과 같이 생성할 수 있다.
TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();

Map 인터페이스 타입 변수에 대입해도 되지만 TreeMap 클래스 타입으로 대입한 이유는 특정 객체를 찾거나 범위 검색과 관련된 메소드를 사용하기 위해서이다.


✅ TreeMap 검색 메소드


✅ 예제 | 특정 Map.Entry 찾기

  • 점수를 키로, 이름을 값으로 해서 무작위로 저장하고 특정 Map.Entry를 찾는 예제
import java.util.Map;
import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        TreeMap<Integer, String> scores = new TreeMap<Integer, String>();
        scores.put(87, "김");
        scores.put(98, "이");
        scores.put(75, "박");
        scores.put(95, "최");
        scores.put(80, "윤");

        Map.Entry<Integer, String> entry = null;

        entry = scores.firstEntry();
        System.out.println("가장 낮은 점수: " + entry.getKey() + "-" + entry.getValue());

        entry = scores.lastEntry();
        System.out.println("가장 높은 점수: " + entry.getKey() + "-" + entry.getValue() + "\n");

        entry = scores.lowerEntry(95);
        System.out.println("95점 아래 점수: " + entry.getKey() + "-" + entry.getValue());

        entry = scores.higherEntry(95);
        System.out.println("95점 위의 점수: " + entry.getKey() + "-" + entry.getValue() + "\n");

        entry = scores.ceilingEntry(95);
        System.out.println("95점 이거나 바로 아래 점수: " + entry.getKey() + "-" + entry.getValue());

        entry = scores.floorEntry(95);
        System.out.println("95점 이거나 바로 위의 점수: " + entry.getKey() + "-" + entry.getValue() + "\n");

        while (!scores.isEmpty()) {
            entry = scores.pollFirstEntry();
            System.out.println(entry.getKey() + "-" + entry.getValue() + " (남은 객체 수: " + scores.size() + ")");
        }

    }
}
  • 실행 결과
가장 낮은 점수: 75-박
가장 높은 점수: 98-이

95점 아래 점수: 87-김
95점 위의 점수: 98-이

95점 이거나 바로 아래 점수: 95-최
95점 이거나 바로 위의 점수: 95-최

75-박 (남은 객체 수: 4)
80-윤 (남은 객체 수: 3)
87-김 (남은 객체 수: 2)
95-최 (남은 객체 수: 1)
98-이 (남은 객체 수: 0)

✅ TreeMap 정렬 메소드

  • descendingKeySet() 메소드는 내림차순으로 정렬된 NavigableSet 객체를 리턴한다.
  • descendingMap() 메소드는 내림차순으로 정렬된 NavigableMap 객체를 리턴한다.
    • NavigableMap은 firstEntry(), lastEntry(), lowerEntry(), higherEntry(), floorEntry(), ceilingEntry() 메소드를 제공하고, 오름차순과 내림차순으로 번갈하며 정렬 순서를 바꾸는 descendingMap() 메소드도 제공한다.
  • 오름차순으로 정렬하고 싶다면 다음과 같이 descendingMap() 메소드를 두 번 호출하면 된다.
NavigableMap<K, V> descendingMap = treeMap.descendingMap();
NavigableMap<K, V> ascendingMap = descendingMap.descendingMap();

✅ 예제 | 객체 정렬하기

import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapExample2 {
    public static void main(String[] args) {
        TreeMap<Integer, String> scores = new TreeMap<Integer, String>();
        scores.put(87, "김");
        scores.put(98, "이");
        scores.put(75, "박");
        scores.put(95, "최");
        scores.put(80, "윤");

        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() + " ");
        }
    }
}
  • 실행 결과
98-이 95-최 87-김 80-윤 75-박 
75-박 80-윤 87-김 95-최 98-이 

✅ TreeMap 범위 검색 메소드

세 가지 메소드 중에서 subMap() 메소드의 사용 방법을 자세히 살펴보도록 하자. subMap() 메소드는 네 개의 매개 변수가 있는데, 시작 키끝 키, 그리고 이 키들의 Map.Entry를 포함할지 여부의 boolean 값을 받는다.

NavigableMap<K, V> subMap = treeMap.subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive);
  • K fromKey: 시작 키
  • boolean fromInclusive: 시작 Map.Entry 포함 여부
  • K toKey: 끝 키
  • boolean toInclusive: 끝 Map.Entry 포함 여부

✅ 예제 | 키로 정렬하고 범위 검색하기

import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;

public class TreeMapExample3 {
    public static void main(String[] args) {
        TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>();
        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);

        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.println(entry.getKey() + "-" + entry.getValue() + "페이지");
        }
    }
}
  • 실행 결과
[c-f 사이의 단어 검색]
cherry-30페이지
description-40페이지
ever-50페이지

2. Comparable과 Comparator

  • TreeSet의 객체와 TreeMap의 키는 저장과 동시에 자동 오름차순으로 정렬되는데,
    • 숫자(Integer, Double) 타입일 경우에는 값으로 정렬하고
    • 문자열(String) 타입일 경우에는 유니코드로 정렬한다.
  • TreeSet과 TreeMap은 정렬을 위해 java.lang.Comparable을 구현한 객체를 요구하는데, Integer, Double, String은 모두 Comparable 인터페이스를 구현하고 있다.
  • 사용자 정의 클래스도 Comparable을 구현한다면 자동 정렬이 가능하다.
    • Comparable에는 compareTo() 메소드가 정의되어 있기 때문에 사용자 정의 클래스에서는 이 메소드를 오버라이딩하여 다음과 같이 리턴 값을 만들어 내야 한다.

✅ 예제 | Comparable 구현 클래스

다음은 나이를 기준으로 Person 객체를 오름차순으로 정렬하기 위해 Comparable 인터페이스를 구현한 것이다.
나이가 적을 경우는 -1을, 동일한 경우는 0을, 클 경우는 1을 리턴하도록 compareTo() 메소드를 재정의하였다.

  • Person
public class Person implements Comparable<Person> {
    public String name;
    public int age;

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

    @Override
    public int compareTo(Person o) {
        if (age < o.age) return -1;
        else if (age == o.age) return 0;
        else return 1;
    }
}
  • ComparableExample
import java.util.Iterator;
import java.util.TreeSet;

public class ComparableExample {
    public static void main(String[] args) {
        TreeSet<Person> treeSet = new TreeSet<Person>();
        treeSet.add(new Person("김", 45));
        treeSet.add(new Person("이", 25));
        treeSet.add(new Person("최", 31));

        Iterator<Person> iterator = treeSet.iterator();
        while (iterator.hasNext()) {
            Person person = iterator.next();
            System.out.println(person.name + ": " + person.age);
        }
    }
}

✅ Comparable 비구현 객체 정렬

  • TreeSet의 객체와 TreeMap의 키가 Comparable을 구현하고 있지 않을 경우에는 저장하는 순간 ClassCastException이 발생한다.
  • TreeSet 또는 TreeMap 생성자의 매개값으로 정렬자(Comparator)를 제공하면 Comparable 비구현 객체도 정렬시킬 수 있다.
TreeSet<E> treeSet = new TreeSet<E>( new AscendingComparator() );
TreeMap<K, V> treeMap = new TreeMap<K, V>( new DescendingComparator() );

정렬자는 Comparator 인터페이스를 구현한 객체를 말하는데, Comparator 인터페이스에는 다음과 같이 메소드가 정의되어 있다.

compare() 메소드는 비교하는 두 객체가 동등하면 0, 비교하는 값보다 앞에 오게 하려면 음수, 뒤에 오게 하려면 양수를 리턴하도록 구현하면 된다.


✅ 예제 | 내림차순 정렬자를 사용하는 TreeSet

다음은 가격을 기준으로 Fruit 객체를 내림차순으로 정렬시키는 정렬자이다.

  • DescendingComparator
import java.util.Comparator;

public class DescendingComparator implements Comparator<Fruit> {
    @Override
    public int compare(Fruit o1, Fruit o2) {
        if (o1.price < o2.price) return 1;
        else if (o1.price == o2.price) return 0;
        else return -1;
    }
}
  • Fruit
public class Fruit {
    public String name;
    public int price;

    public Fruit(String name, int price) {
        this.name = name;
        this.price = price;
    }
}
  • ComparatorExample
package book;

import java.util.Iterator;
import java.util.TreeSet;

public class ComparatorExample {
    public static void main(String[] args) {
        TreeSet<Fruit> treeSet = new TreeSet<Fruit>(new DescendingComparator());    //내림차순 정렬자 제공
        treeSet.add(new Fruit("포도", 3000));
        treeSet.add(new Fruit("수박", 10000));
        treeSet.add(new Fruit("딸기", 6000));

        Iterator<Fruit> iterator = treeSet.iterator();
        while (iterator.hasNext()) {
            Fruit fruit = iterator.next();
            System.out.println(fruit.name + ": " + fruit.price);
        }
    }
}
  • 실행 결과
수박: 10000
딸기: 6000
포도: 3000

[ 참고자료 ]

이것이 자바다 책
http://tcpschool.com/java/java_collectionFramework_map

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글