๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โŒจ๏ธ Language/Java

[Java] HashMap vs TreeMap vs LinkedHashMap, ์–ด๋–ค ์ƒํ™ฉ์—์„œ ์จ์•ผ ํ• ๊นŒ?

by hyebin (Helia) 2025. 6. 11.
๋ฐ˜์‘ํ˜•

 


๋ชฉ์ฐจ

     

     

    ์ง€๊ธˆ๊นŒ์ง€ List์™€ Set์˜ ๊ตฌํ˜„์ฒด๋“ค์„ ์‚ดํŽด๋ดค๋‹ค๋ฉด, ์ด์ œ Key-Value ๊ตฌ์กฐ์˜ Map ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์ฒด๋“ค์„ ๋น„๊ตํ•ด ๋ณผ ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

    Map์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ‚ค-๊ฐ’ ์Œ์œผ๋กœ ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ๋กœ, ๋น ๋ฅธ ๊ฒ€์ƒ‰๊ณผ ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

     

    ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Java Map ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ, ๋Œ€ํ‘œ ๊ตฌํ˜„์ฒด์ธ HashMap, TreeMap, LinkedHashMap์˜ ํŠน์ง•๊ณผ ์„ฑ๋Šฅ์„ ํ•œ๋ˆˆ์— ๋น„๊ตํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


    ๐Ÿ—‚๏ธ Map ์ธํ„ฐํŽ˜์ด์Šค๋ž€?

    Map์€ Key-Value ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ๋กœ, Key๋Š” ์ค‘๋ณต์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉฐ Value๋Š” ์ค‘๋ณต์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

    Map<String, Integer> map = new HashMap<>();
    map.put("Apple", 1000);   // Key: "Apple", Value: 1000
    map.put("Banana", 1500);  // Key: "Banana", Value: 1500
    
    Integer price = map.get("Apple");  // 1000 ๋ฐ˜ํ™˜

     

    ์ฃผ์š” ํŠน์ง•

    • Key๋Š” ์ค‘๋ณต ๋ถˆ๊ฐ€, Value๋Š” ์ค‘๋ณต ๊ฐ€๋Šฅ
    • Key๋ฅผ ํ†ตํ•œ ๋น ๋ฅธ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅ
    • null ํ—ˆ์šฉ ์—ฌ๋ถ€์™€ ์ˆœ์„œ ๋ณด์žฅ ์—ฌ๋ถ€๋Š” ๊ตฌํ˜„์ฒด๋งˆ๋‹ค ๋‹ค๋ฆ„

     

    Map์˜ ํ•ต์‹ฌ ๋ฉ”์„œ๋“œ

    ๋ฉ”์„œ๋“œ ์„ค๋ช…
    put(K key, V value) ํ‚ค-๊ฐ’ ์Œ ์ถ”๊ฐ€ ๋˜๋Š” ์ˆ˜์ •
    get(Object key) ํ‚ค๋กœ ๊ฐ’ ์กฐํšŒ
    remove(Object key) ํ‚ค-๊ฐ’ ์Œ ์‚ญ์ œ
    containsKey() ํ‚ค ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ
    keySet() ๋ชจ๋“  ํ‚ค ๋ฐ˜ํ™˜
    values() ๋ชจ๋“  ๊ฐ’ ๋ฐ˜ํ™˜
    entrySet() ๋ชจ๋“  ํ‚ค-๊ฐ’ ์Œ ๋ฐ˜ํ™˜

    ๐Ÿ”Ž HashMap vs TreeMap vs LinkedHashMap

    HashMap 

    HashMap์€ ํ•ด์‹œ ํ…Œ์ด๋ธ” ๊ธฐ๋ฐ˜์˜ Map์œผ๋กœ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ตฌํ˜„์ฒด์ž…๋‹ˆ๋‹ค.

    Map<String, Integer> hashMap = new HashMap<>();
    hashMap.put("Banana", 1500);
    hashMap.put("Apple", 1000);
    hashMap.put("Cherry", 2000);
    
    System.out.println(hashMap);
    // ์ถœ๋ ฅ ์˜ˆ: {Cherry=2000, Apple=1000, Banana=1500} - ์ˆœ์„œ ๋ณด์žฅ ์•ˆ๋จ
    • ์ˆœ์„œ ๋ณด์žฅ ์•ˆ๋จ: ํ•ด์‹œ๊ฐ’์— ๋”ฐ๋ผ ์ €์žฅ ์ˆœ์„œ ๊ฒฐ์ •
    • ๋น ๋ฅธ ์„ฑ๋Šฅ: ํ‰๊ท  O(1) ์‹œ๊ฐ„๋ณต์žก๋„
    • null ํ—ˆ์šฉ: Key์™€ Value ๋ชจ๋‘ null ๊ฐ€๋Šฅ (Key๋Š” ํ•˜๋‚˜๋งŒ)
    • ๋น„๋™๊ธฐ: ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜์ง€ ์•Š์Œ

     

    TreeMap 

    TreeMap์€ ์ด์ง„ ๊ฒ€์ƒ‰ ํŠธ๋ฆฌ(Red-Black Tree) ๊ธฐ๋ฐ˜์œผ๋กœ ์ž๋™ ์ •๋ ฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    Map<String, Integer> treeMap = new TreeMap<>();
    treeMap.put("Banana", 1500);
    treeMap.put("Apple", 1000);
    treeMap.put("Cherry", 2000);
    
    System.out.println(treeMap);
    // ์ถœ๋ ฅ: {Apple=1000, Banana=1500, Cherry=2000} - ํ‚ค ๊ธฐ์ค€ ์ž๋™ ์ •๋ ฌ
    • ์ž๋™ ์ •๋ ฌ: ํ‚ค ๊ธฐ์ค€์œผ๋กœ ์ž๋™ ์ •๋ ฌ ์œ ์ง€
    • NavigableMap ๊ตฌํ˜„: ๋ฒ”์œ„ ๊ฒ€์ƒ‰, ๊ทผ์ ‘ ํ‚ค ์ฐพ๊ธฐ ๊ฐ€๋Šฅ
    • null ๋ถˆ๊ฐ€: Key์— null ์‚ฌ์šฉ ๋ถˆ๊ฐ€ (Value๋Š” ๊ฐ€๋Šฅ)
    • ๋А๋ฆฐ ์„ฑ๋Šฅ: O(log n) ์‹œ๊ฐ„๋ณต์žก๋„

     

    LinkedHashMap 

    LinkedHashMap์€ ํ•ด์‹œ ํ…Œ์ด๋ธ” + ์ด์ค‘ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ๊ตฌ์กฐ๋กœ, ์ž…๋ ฅ ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

    Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
    linkedHashMap.put("Banana", 1500);
    linkedHashMap.put("Apple", 1000);
    linkedHashMap.put("Cherry", 2000);
    
    System.out.println(linkedHashMap);
    // ์ถœ๋ ฅ: {Banana=1500, Apple=1000, Cherry=2000} - ์‚ฝ์ž… ์ˆœ์„œ ์œ ์ง€
    • ์‚ฝ์ž… ์ˆœ์„œ ์œ ์ง€: ์š”์†Œ๊ฐ€ ์ถ”๊ฐ€๋œ ์ˆœ์„œ๋ฅผ ๊ธฐ์–ต
    • HashMap๊ณผ ์œ ์‚ฌํ•œ ์„ฑ๋Šฅ: ํ‰๊ท  O(1) ์‹œ๊ฐ„๋ณต์žก๋„
    • null ํ—ˆ์šฉ: Key์™€ Value ๋ชจ๋‘ null ๊ฐ€๋Šฅ
    • ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ: ์ˆœ์„œ ์œ ์ง€๋ฅผ ์œ„ํ•œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ์˜ค๋ฒ„ํ—ค๋“œ

     

    ์„ฑ๋Šฅ๊ณผ ํŠน์ง• ๋น„๊ต

    ํŠน์„ฑ HashMap TreeMap LinkedHashMap
    ์ˆœ์„œ ๋ณด์žฅ โŒ โœ… (์ •๋ ฌ) โœ… (์‚ฝ์ž… ์ˆœ์„œ)
    ์ •๋ ฌ โŒ โœ… (์ž๋™) โŒ
    null Key โœ… (1๊ฐœ) โŒ โœ… (1๊ฐœ)
    null Value โœ… โœ… โœ…
    ๊ฒ€์ƒ‰/์‚ฝ์ž…/์‚ญ์ œ O(1) ํ‰๊ท  O(log n) O(1) ํ‰๊ท 
    ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์ ์Œ ์ค‘๊ฐ„ ๋งŽ์Œ
    ์‚ฌ์šฉ ๋ชฉ์  ๋น ๋ฅธ ๊ฒ€์ƒ‰ ์ •๋ ฌ/๋ฒ”์œ„ ๊ฒ€์ƒ‰ ์ˆœ์„œ ์œ ์ง€, LRU ์บ์‹œ ๋“ฑ

    ๐Ÿ’ป ์‹ค์ „ ํ™œ์šฉ ์˜ˆ์ œ

    1๏ธโƒฃ HashMap - ์‚ฌ์šฉ์ž ์ •๋ณด ๊ด€๋ฆฌ

    Map<String, String> userMap = new HashMap<>();
    userMap.put("user001", "๊น€์ž๋ฐ”");
    userMap.put("user002", "์ด์ฝ”๋”ฉ");
    
    String userName = userMap.get("user001");  // O(1) ๋น ๋ฅธ ๊ฒ€์ƒ‰

     

    2๏ธโƒฃ TreeMap - ์„ฑ์  ๊ด€๋ฆฌ ์‹œ์Šคํ…œ

    Map<String, Integer> scoreMap = new TreeMap<>();
    scoreMap.put("ํ™๊ธธ๋™", 85);
    scoreMap.put("๊น€์˜ํฌ", 92);
    
    System.out.println(scoreMap);  // Key ๊ธฐ์ค€ ์ •๋ ฌ

     

    ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ์˜ˆ์‹œ

    TreeMap<String, Integer> treeScoreMap = (TreeMap<String, Integer>) scoreMap;
    String firstStudent = treeScoreMap.firstKey();  // ๊ฐ€์žฅ ์ž‘์€ Key
    String higherStudent = treeScoreMap.higherKey("๊น€์˜ํฌ");  // ๋ฐ”๋กœ ๋‹ค์Œ Key

     

    3๏ธโƒฃ LinkedHashMap - ์›น ์บ์‹œ ์‹œ์Šคํ…œ (LRU)

    public class LRUCache<K, V> extends LinkedHashMap<K, V> {
        private final int maxSize;
    
        public LRUCache(int maxSize) {
            super(16, 0.75f, true);  // accessOrder=true
            this.maxSize = maxSize;
        }
    
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maxSize;
        }
    }
    
    // ์‚ฌ์šฉ ์˜ˆ
    LRUCache<String, String> cache = new LRUCache<>(3);
    cache.put("page1", "๋‚ด์šฉ1");
    cache.put("page2", "๋‚ด์šฉ2");
    cache.get("page1");  // page1์ด ์ตœ๊ทผ์œผ๋กœ ์ด๋™
    cache.put("page4", "๋‚ด์šฉ4");  // ์˜ค๋ž˜๋œ page2 ์ œ๊ฑฐ

    ๐Ÿ” null ํ—ˆ์šฉ ์—ฌ๋ถ€์™€ ๋™์‹œ์„ฑ ์ฃผ์˜

    • HashMap: Key, Value ๋ชจ๋‘ null ํ—ˆ์šฉ
    • TreeMap: Key๋Š” null ๋ถˆ๊ฐ€ (์˜ˆ์™ธ ๋ฐœ์ƒ), Value๋Š” null ํ—ˆ์šฉ
    • LinkedHashMap: Key, Value ๋ชจ๋‘ null ํ—ˆ์šฉ

     

    ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ๋Š” ์ง์ ‘ ๋™๊ธฐํ™”ํ•˜๊ฑฐ๋‚˜ ConcurrentHashMap์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    Map<String, Integer> safeMap = Collections.synchronizedMap(new HashMap<>());
    // ํ˜น์€
    Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();

    ๐Ÿ› ๏ธ Map ์„ ํƒ ๊ฐ€์ด๋“œ

    • ๐Ÿ”ธ HashMap → ๊ฐ€์žฅ ์ผ๋ฐ˜์ , ๋น ๋ฅธ ์„ฑ๋Šฅ์ด ํ•„์š”ํ•  ๋•Œ
    • ๐Ÿ”ธ TreeMap → Key ์ •๋ ฌ/๋ฒ”์œ„ ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•  ๋•Œ
    • ๐Ÿ”ธ LinkedHashMap → ์‚ฝ์ž… ์ˆœ์„œ ์œ ์ง€, ์บ์‹œ ๊ตฌํ˜„

    ์‹ค๋ฌด์—์„œ๋Š” 90% ์ด์ƒ์ด HashMap์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ์—๋งŒ TreeMap, LinkedHashMap์„ ์„ ํƒํ•ด๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค.


    ๐Ÿ™Œ ๋งˆ๋ฌด๋ฆฌ

    ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Java์˜ Map ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ทธ ๋Œ€ํ‘œ ๊ตฌํ˜„์ฒด(HashMap, TreeMap, LinkedHashMap)์˜ ๊ตฌ์กฐ์™€ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ด ๋ดค์Šต๋‹ˆ๋‹ค.

    Map์€ ์ž๋ฐ” ๊ฐœ๋ฐœ์—์„œ ๋ฐ์ดํ„ฐ ์ €์žฅ์˜ ํ•ต์‹ฌ ๋„๊ตฌ์ธ ๋งŒํผ, ๊ฐ ๊ตฌํ˜„์ฒด์˜ ํŠน์ง•์„ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

     

    ๋‹ค์Œ ๊ธ€์—์„œ๋Š” Iterator์™€ Stream API๋ฅผ ํ™œ์šฉํ•œ ์ปฌ๋ ‰์…˜ ์ˆœํšŒ ๋ฐฉ๋ฒ•์„ ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    ๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ๋” ์•Œ๊ณ  ์‹ถ์€ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ์„ธ์š”! ๐Ÿ˜Š

    ๋ฐ˜์‘ํ˜•