Nick Dev

[JAVA] Map, HashMap, TreeMap, Properties 본문

Java

[JAVA] Map, HashMap, TreeMap, Properties

Nick99 2024. 12. 11. 12:14
반응형
반응형

Map

Map이란?

  1. key : value 쌍으로 존재
    • 둘 중 하나만 존재할 수 없음
  2. key는 해당 Map에서 unique해야만 함
  3. value는 중복되어도 상관X

Map 인터페이스를 구현한 주요 클래스

  1. HashMap
    • 가장 많이 사용
  2. TreeMap
  3. LinkedHashMap
  4. HashTable
    • Map과 다름
    • HashTable은 JDK 1.0부터 있었던 클래스
    • HashMap은 JDK 1.2부터 만들어짐
      • 이때 Collection 인터페이스 만들어짐
      기능 HashMap HashTable
      key, value에 null 저장 가능 여부 가능 불가능
      Thread-safe Not Safe Safe
      데이터 처리 Collection View Enumeration 객체
  • 1,2,3 클래스들은 전부 Not Thread-safe임
    • Map m = Collection.synchronizedMap(new HashMap(…));
    • 이렇게 선언해야 여러 thread에서 동시에 접근해도 문제 발생X

Collection View(HashMap)와 Enumeration(HashTable)의 차이

Collection View(HashMap)

  • keySet(), values(), entrySet() 등의 메소드를 통해 Map의 내용을 Collection View 형태로 볼 수 있음
  • keySet(): Map의 모든 키를 Set 객체로 반환
  • values(): Map의 모든 값을 Collection 객체로 반환
  • entrySet(): Map의 모든 key-value 쌍을 Entry 객체의 Set으로 반환
import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("One", 1);
        map.put("Two", 2);
        map.put("Three", 3);

        // Collection View를 사용한 반복
        for (Map.**Entry<String, Integer>** entry : map.**entrySet()**) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }
}

Enumeration(HashTable)

  • keys() 메소드는 Map의 키에 대한 Enumeration을 반환하고, elements() 메소드는 값에 대한 Enumeration을 반환
    • EnumerationIterator의 구식 버전
  • keys(): Map의 모든 키에 대한 Enumeration 객체를 반환합니다.
  • elements(): Map의 모든 값에 대한 Enumeration 객체를 반환합니다
import java.util.Hashtable;
import java.util.Enumeration;

public class HashtableExample {
    public static void main(String[] args) {
        Hashtable<String, Integer> table = new Hashtable<>();
        table.put("One", 1);
        table.put("Two", 2);
        table.put("Three", 3);

        // Enumeration을 사용한 키 반복
        **Enumeration<String> keys = table.keys();**
        while (**keys.hasMoreElements()**) {
            String key = keys.**nextElement()**;
            System.out.println(key + " = " + table.get(key));
        }
    }
}

HashMap

HashMap의 상속 관계

  • java.util.Object
    • java.util.AbstractMap<K, V>
      • java.util.HashMap<K, V>
  • 대부분의 주요 메서드들은 AbstractMap 클래스가 구현함

특정 클래스를 HashMap의 key로 사용할 때 주의점

  • 기본적으로 key는 기본 자료형, 참조 자료형 모두 가능
  • 직접 특정 클래스 생성 후, 그 클래스를 key로 사용하면 Object클래스의 hashcode(), equals() 메소드 잘 구현해야 함
  • HashMap에 객체가 들어가면 그 객체의 hashcode() 결과 값에 따라 bucket이 만들어짐
    • 만약 서로 다른 key 저장되었는데, hashcode() 결과 값이 동일하면 같은 bucket에 여러 개의 값이 들어갈 수 있음(=충돌)
    • get() 호출 시, hashcode() 결과 확인 후, bucket에 여러 개 data 존재할 경우 equals()로 동일한 값을 찾게 된다
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Book {
    private String title;
    private String author;

    public Book(String title, String author) {
        this.title = title;
        this.author = author;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Objects.equals(title, book.title) &&
                Objects.equals(author, book.author);
    }

    @Override
    public int hashCode() {
        return Objects.hash(title, author);
    }
}

public class HashMapExample {
    public static void main(String[] args) {
        Map<Book, Integer> books = new HashMap<>();
        Book book1 = new Book("Effective Java", "Joshua Bloch");
        Book book2 = new Book("Java Concurrency in Practice", "Brian Goetz");

        books.put(book1, 1);
        books.put(book2, 2);

        // 동일한 속성을 가진 새 객체를 생성
        Book book3 = new Book("Effective Java", "Joshua Bloch");

        // book3는 book1과 같은 속성을 가지므로, 같은 hashCode를 가질 것이고 equals()에서 true를 반환할 것이다.
        System.out.println("Book1's hashcode: " + book1.hashCode());
        System.out.println("Book3's hashcode: " + book3.hashCode());
        System.out.println("Book1 and Book3 are equal: " + book1.equals(book3));
        System.out.println("Number of copies of 'Effective Java': " + books.get(book3));
    }
}
  • Book 클래스는 titleauthor 필드를 기반으로 hashCode()equals() 메소드를 재정의
    • 같은 제목과 저자를 가진 Book 객체들은 동일한 해시코드 → equals() 메소드에서 true를 반환함
  • book1book3이 같은 속성
    • book3을 사용하여 book1을 키로 가지는 값을 성공적으로 검색 가능
  • 결론 : hashcode()equals() 구현해라

Map에서 존재하지 않는 key로 get()하면?

  • Exception이 아닌 **null**을 반환해버린다

HashMap은 순서가 중요?!

  • Set, Map 자료 구조는 순서가 중요하지 않다

key만 알고 싶어!

  • **keySet()**을 통해 해당 hashMap에 어떤 key들이 있는지 알 수 있음
  • 리턴 타입 : Set
    • Set의 제네릭 타입은 선언한 HashMap의 Key의 제네릭 타입
    • HashMap<**String**, Integer> map = new HashMap<>();
    • **Set<String>** keySet = map.**keySet()**;

value만 알고 싶어!

  • **values()**을 통해 해당 hashMap에 어떤 value들이 있는지 알 수 있음
  • 리턴 타입 : Collection
    • Collection의 제네릭 타입은 선언한 HashMap의 value의 제네릭 타입
    • HashMap<String, **Integer**> map = new HashMap<>();
    • **Collection<Integer>** values = map.**values()**;

Key, Value 세트로 알고 싶어!

  • **entrySet()**을 통해 Key와 Value를 세트로 알 수 있다
  • 리턴 타입 : Set<Map.Entry<>>
    • Set타입으로 리턴, Set내에는 Entry타입으로 데이터가 저장되어 있음
    • HashMap<String, Integer> map = new HashMap<>();
    • Set<**Map.Entry<String, Integer>**> entries = new map.entrySet();

containsKey(obj), containsValue(obj)

  • 매개 변수로 넘긴 키, 값이 존재하는지 true, false로 알려줌

containsKey()를 써야될까?

  • 해당 key가 존재하는지 확인할 때 get(obj) == null방식으로도 확인 가능한데 왜 containsKey()를 쓰는게 효과적일까?
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("1", null);
if (hashMap.get("1") == null) {
    System.out.println(hashMap.get("1"));
} 
  • 위의 경우, 1 에 대한 value에 명시적으로 null이 저장되어 있다면 **hashMap.get("1") == null 이 구문은 true가 된다**
  • 우리의 의도는 key가 1 인게 있는지 없는지 여부
    • 존재하지만 존재하지 않는 것으로 잘못 해석될 수 있음
  • 성능 상의 차이는 거의 없음
  • 명확한 의도정확한 결과를 위해 containsKey() 쓰는게 좋다

 

TreeMap

TreeMap이란?

  • Key를 정렬해서 저장하고, Key목록 가져와서 출력하면 정렬된 순서대로 제공

언제 TreeMap을 쓸까?

  • 정렬된 Key 목록을 원할 때
  • 매우 많은 데이터일 때는 HashMap보다 느림 / 정렬된 Key 필요 없으면 HashMap이 더 성능 좋음
  • 100~1000건 데이터 처리 & 정렬 필요 ⇒ HashMap보다 TreeMap 사용

정렬 순서

  • 숫자 > 알파벳 대문자 > 알파벳 소문자 > 한글 순서로 정렬되서 저장

TreeMap은 어떻게 Key를 정렬할까?

  • SortedMap 인터페이스를 구현했기 때문에 가능
    • SortedMap 인터페이스를 구현한 클래스들은 모두 키가 정렬되어 있어야 함
  • red-black Tree 사용해 구현되어 있음
    • O(logN)의 시간복잡도
    • 성능 좋은편

그 때 그때 정렬해서 쓰면 되지 왜 Key를 정렬한 클래스가 존재하는거야?

  • Key가 정렬되어 있다면 맨 앞, 뒤 또는 특정 Key 앞, 뒤에 있는 Key를 알 수 있는 메서드 제공 가능
    • firstKey() : 가장 앞에 있는 Key
    • lastKey() : 가장 뒤에 있는 Key
    • higherKey(K key) : 특정 Key 바로 뒤에 있는 Key
    • lowerKey(K key) : 특정 Key 바로 앞에 있는 Key
      ~~Map<String, Integer> map = new TreeMap<>();
      String firstKey = map.firstKey():~~
    • ⚠️ 이 메서드들 쓰려면 Map 인터페이스로 UpCasting하면 안된다 ⚠️
    • TreeMap*<String, Integer> treeMap = new TreeMap<>();
      String firstKey = map.firstKey():

Properties 클래스

특징

  • HashTableextends 했기에 Map 인터페이스에서 제공하는 모든 메서드 사용 가능
  • 자바에서 시스템 속성 알려주는 클래스
  • Properties prop = System.getProperties();
    • System 클래스에 static메서드인 getProperties() 호출하면 Properties객체 리턴

굳이 Properties클래스 사용하는 이유는?!

  • Properties에서 추가로 제공하는 메서드들 때문에
  • 파일에서 읽어오고, 파일에 저장하고, 저장할 때 주석도 같이 넣어주는 메서드들 때문에
    • load(), store(), storeToXML()

Java의 자료 구조 정리

  • Collection
    • List : 배열처럼 목록 처리
      • ArrayList
      • LinkedList
    • Set : 중복 X, 순서 X
      • HashSet
        • LinkedHashSet
      • TreeSet
    • Queue : 들어온 순서대로 처리(FIFO)
      • LinkedList
      • PriorityQueue

  • Map : Key-Value 형식으로 저장
    • HashMap : 가장 많이 씀
      • LinkedHashMap
    • TreeMap
    • 메서드
      • ketSet()으로 Set타입의 key목록 리턴
      • valuse()Collection타입의 valuse 목록 리턴
        • for loop OR Iterator로 데이터 처리

직접해 봅시다

package second_chapter06_map;

import java.util.Hashtable;
import java.util.Random;
import java.util.Set;

public class RandomNumberStatistics {
    public static void main(String[] args) {
        RandomNumberStatistics sample = new RandomNumberStatistics();
        sample.getRandomNumberStatistics();
    }

    private final int DATA_BOUNDARY = 50;

    Hashtable<Integer, Integer> hashtable = new Hashtable<>();

    public void getRandomNumberStatistics() {
        for (int i = 0; i < 5000; i++) {
            Random random = new Random();
            putCurrentNumber(random.nextInt(1, DATA_BOUNDARY+1));
        }
        printStatistics();
    }

    public void putCurrentNumber(int tempNumber) {
        if (hashtable.containsKey(tempNumber)) {
            hashtable.put(tempNumber, hashtable.get(tempNumber)+1);
        }
        else {
            hashtable.put(tempNumber, 1);
        }
    }

    public void printStatistics() {
        Set<Integer> keySet = hashtable.keySet();
        for (int key:keySet) {
            System.out.print(key + "=" + hashtable.get(key) + "\t");
            // key가 1로 끝날 때마다 줄바꿈 넣자 -> 그래야 한 줄에 10개씩 이쁘게 출력
            if (key%10-1==0) System.out.println();
        }
    }
}

정리해 봅시다

1. Map 형태의 자료 구조는 무엇과 무엇으로 구성되어 있나요

  • Key와 Value로 구성

2. Map에서 데이터를 저장하는 메서드

  • put()

3. Map에서 특정 키에 할당된 값을 가져오는 메서드

  • get()

4. Map에서 특정 키와 관련된 키와 데이터를 지우는 메서드

  • remove()

5. Map에서 키의 목록을 가져오는 메서드

  • keySet()

6. Map에 저장되어 있는 데이터의 크기를 가져오는 메서드

  • size()

7. HashMap과 HashTable 중에서 키나 값에 null을 저장할 수 있는 것은?

  • HashMap

8. HashMap과 HashTable 중 여러 쓰레드에서 동시 접근해도 문제 없는 것은?

  • HashTable

9. HashMap에서 특정 키가 존재하는지 확인하는 메서드

  • containsKey()

10. 키가 저장되면서 정렬되는 Map은?

  • TreeMap

11. Properties 클래스는 어떤 클래스를 확장한 것인가?

  • HashTable

12. Properties 클래스의 객체에 있는 데이터를 파일로 저장할 때에는 어떤 메서드들을 사용하면 되나요?

  • store(), storeToXML() 메서드 사용
반응형