Java集合-Map

Map集合

特色

  • 雙列集合<K,V>
  • Key 和 Value一一對應
  • K不容許重複,V能夠重複
  • K,V的數據類型能夠相同,也可不一樣

經常使用方法

方法 描述
V put(K key, V value) 將指定的值與該映射中的指定鍵相關聯(可選操做)。
V remove(Object key) 若是存在(從可選的操做),從該地圖中刪除一個鍵的映射。
V get(Object key) 返回到指定鍵所映射的值,或 null若是此映射包含該鍵的映射。
boolean containsKey(Object key) 若是此映射包含指定鍵的映射,則返回 true 。
Set keySet() 返回此地圖中包含的鍵的Set視圖。
Set<Map.Entry<K,V>> entrySet() 返回此地圖中包含的映射的Set視圖。

內部接口:Entry<K,V>java

做用:當Map集合建立,就會建立一個Entry對象,用來存儲鍵值對。面試

測試代碼

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

/**
 * 測試Map集合的經常使用方法
 * add、remove、get、containKey、keySet、entrySet
 */
public class TestMap {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        // put的返回值是被替代的V,若是原來沒有就是null
        System.out.println(map.put("1001", "張三"));
        System.out.println(map.put("1001", "張三丰"));
        map.put("1002", "李四");
        map.put("1003", "王五");
        System.out.println(map);

        // remove返回被刪除的V,Key不存在則返回null
        System.out.println(map.remove("1005"));
        System.out.println(map.remove("1003"));
        System.out.println(map);

        // get返回key對應的value, 無則null
        System.out.println(map.get("1002"));
        System.out.println(map.get("1003"));
        // containsKey 包含key返回true
        System.out.println(map.containsKey("1002"));
        System.out.println(map.containsKey("1003"));

        System.out.println("遍歷方式1:將全部key取出來存到set集合");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println("K:"+key+" V:"+map.get(key));
        }

        System.out.println("遍歷方式2:將全部Entry<K,V>對象取出來存到set集合");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}

運行結果數組

null
張三
{1003=王五, 1002=李四, 1001=張三丰}
null
王五
{1002=李四, 1001=張三丰}
李四
null
true
false
遍歷方式1:將全部key取出來存到set集合
K:1002 V:李四
K:1001 V:張三丰
遍歷方式2:將全部Entry<K,V>對象取出來存到set集合
1002=李四
1001=張三丰

HashMap(重點)

基於哈希表的實現的Map接口。 此實現提供了全部可選的Map操做,並容許null值和null鍵。安全

底層原理

底層是哈希表,查詢速度超級快!多線程

JDK8以前:數組+鏈表ide

JDK8以後:數組+鏈表 / 數組+紅黑樹學習

HashMap面試須要更深的理解,深刻底層!測試

目前停留於瞭解原理,掌握運用。this

測試代碼

存儲自定義對象

首先建立一個學生類線程

package map;

public class Student{
    private int id;
    private String name;
    private int age;

    public Student() {
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

測試類

package map;
import java.util.HashMap;

/**
 * 若以自定義類做鍵存放,必須說明鍵重複的規則
 * 實體類重寫hashCode方法和equals方法
 * 好比學生類中,同名不必定是同一個對象,同窗號說明是同一個學生
 */
public class TestHashMap {
    public static void main(String[] args) {
        Student student1 = new Student(1001, "張三", 18);
        Student student2 = new Student(1002, "李四", 17);
        Student student3 = new Student(1001, "張三", 19);
        Student student4 = new Student(1004, "李四", 20);
        HashMap<Student, String> map = new HashMap<>();
        map.put(student1, "第一名");
        map.put(student2, "第二名");
        map.put(student3, "第三名");
        map.put(student4, "第四名");
        System.out.println(map);
    }
}

運行結果

{Student{id=1004, name='李四', age=20}=第四名, Student{id=1002, name='李四', age=17}=第二名, Student{id=1001, name='張三', age=18}=第一名, Student{id=1001, name='張三', age=19}=第三名}

此時觀察student1 和 student3 的學號相同,對象重複,爲何還會添加到map中呢?

image-20200709162818912

有提示要重寫Student類的hashCode方法和equals方法。

修改後的Student.java

package map;

import java.util.Objects;

public class Student{
    private int id;
    private String name;
    private int age;

    public Student() {
    }

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

    // ...get/set省略
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

再次運行測試類的代碼獲得的結果

{Student{id=1001, name='張三', age=18}=第三名, Student{id=1002, name='李四', age=17}=第二名, Student{id=1004, name='李四', age=20}=第四名}

分析:第一次添加1001學生時,value是第一名,再次遇到1001學生,覆蓋了原來的value

深刻理解HashMap

更加詳細的學習會寫到另外一篇

LinkedHashMap

哈希表和鏈表實現的Map接口,具備可預測的迭代次序。有序集合!

public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>{}

底層原理

  • 哈希表 -> 繼承自HashMap
  • 鏈表 -> 記錄元素順序

測試代碼

import java.util.*;

public class TestLinkedHashMap {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put("1001", "張三");
        map.put("1002", "李四");
        map.put("1003", "王五");
        map.put("1004", "趙六");
        System.out.println("map的數據無序,取出的結果與存入順序不一致:");
        System.out.println(map);
        System.out.println();

        LinkedHashMap<String, String> link = new LinkedHashMap<>();
        link.put("1001", "張三");
        link.put("1002", "李四");
        link.put("1003", "王五");
        link.put("1004", "趙六");
        System.out.println("在map外層加一層鏈表記錄元素存入的順序:");
        System.out.println(link);
    }
}

運行結果

map的數據無序,取出的結果與存入順序不一致:
{1004=趙六, 1003=王五, 1002=李四, 1001=張三}

在map外層加一層鏈表記錄元素存入的順序:
{1001=張三, 1002=李四, 1003=王五, 1004=趙六}

Hashtable

Hashtable 和 Vector 同樣都是上古級別的集合,在jdk1.2版本以後被更先進的HashMap及ArrayList集合取代。

值得注意的是,Hashtable的子類Properties目前依舊活躍着,它是惟一和IO流結合的集合。

Hashtable 與 HashMap 的區別

底層都是哈希表,元老級更注重線程安全。

Hashtable

  • 線程安全—>單線程集合—>速度慢
  • 不能存儲 null ,K,V都不能夠

HashMap

  • 線程不安全—>多線程集合—>速度快
  • 能夠存儲 null
    • K 只能存儲一個 null ,保證K的惟一性
    • V 能夠存儲任意個 null

測試代碼

import java.util.HashMap;
import java.util.Hashtable;

public class TestHashtable {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put(null, null);
        map.put("A", null);
        map.put(null, "B");
        System.out.println(map);

        Hashtable<String, String> table = new Hashtable<>();
        //table.put(null, null);//NullPointerException
        //table.put("A", null);//NullPointerException
        //table.put(null, "B");//NullPointerException
        System.out.println(table);
    }
}

運行結果

{null=B, A=null}
{}

HashMap中以null做爲鍵時,再次添加null的鍵會覆蓋以前的value值。

Hashtable只要涉及到null,就會報空指針異常。

集合練習

  1. 判斷字符串中某個字符出現的次數

    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    /**
     * 判斷字符串中某個字符出現的次數
     */
    public class MapPractice {
        public static void main(String[] args) {
            Map<Character, Integer> map = new LinkedHashMap<>();
    //        Scanner sc = new Scanner(System.in);
    //        String str = sc.nextLine();
            String str = "aaabbcdaabb";
            char[] chars = str.toCharArray();
            for (char ch : chars) {
                if (map.get(ch) == null) {
                    map.put(ch, 1);
                } else {
                    int count = map.get(ch);
                    map.put(ch, ++count);
                }
            }
            // 使用linkedHashMap保證從前日後統計的順序
            System.out.println(map);
        }
    }
  2. 鬥地主發牌

    import java.util.*;
    
    /**
     * 撲克發牌並按大小排序
     */
    public class PracticePoker {
        public static void main(String[] args) {
            // 存放索引的map,一個索引對應一張牌,按照必定規則定製
            Map<Integer, String> map = new HashMap<>();
    
            // 存放牌色♠♥♦♣、數字的列表
            List<String> colors = new ArrayList<>();
            List<String> numbers = new ArrayList<>();
            Collections.addAll(colors, "♠", "♥", "♣", "♦");
            Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
    
            // 創建索引與牌的映射 0-"大王" 1-"小王" 2-"2♠" 3-"2♥" ...
            int idx = 0;
            map.put(idx++, "大王");
            map.put(idx++, "小王");
            for (String number : numbers) {
                for (String color : colors) {
                    map.put(idx++, number + color);
                    //System.out.print(number + color + " ");
                }
            }
    
            // 發牌發的是索引,便於比大小 , 由於set是無序的,不能洗牌
            Set<Integer> idxSet = map.keySet();
            ArrayList<Integer> idxList = new ArrayList<>(idxSet);
            // System.out.println("洗牌前" + idxList);
            Collections.shuffle(idxList);
            // System.out.println("洗牌後" + idxList);
    
            // 玩家的牌用列表接收,便於排序
            ArrayList<Integer> playA = new ArrayList<>();
            ArrayList<Integer> playB = new ArrayList<>();
            ArrayList<Integer> playC = new ArrayList<>();
            ArrayList<Integer> special = new ArrayList<>();
    
            // 發牌
            special.add(idxList.get(0));
            special.add(idxList.get(1));
            special.add(idxList.get(2));
            for (int i = 3; i < idxList.size(); i++) {
                if (i % 3 == 0) {
                    playA.add(idxList.get(i));
                } else if (i % 3 == 1) {
                    playB.add(idxList.get(i));
                } else if (i % 3 == 2) {
                    playC.add(idxList.get(i));
                }
            }
            // 排序
            Collections.sort(playA);
            Collections.sort(playB);
            Collections.sort(playC);
            Collections.sort(special);
    
            // 看牌
            lookPoker("劉德華", playA, map);
            lookPoker("周潤發", playB, map);
            lookPoker("周星馳", playC, map);
            lookPoker("底牌", special, map);
        }
    
        public static void lookPoker(String name, List<Integer> idxList, Map<Integer, String> poker) {
            System.out.print(name + "的牌爲:");
            for (Integer idx : idxList) {
                System.out.print(poker.get(idx) + " ");
            }
            System.out.println();
        }
    }

    結果

    劉德華的牌爲:2♥ 2♦ A♥ A♣ A♦ K♠ K♦ Q♥ J♠ 10♠ 8♥ 8♦ 7♠ 6♣ 5♣ 4♣ 3♥ 
    周潤發的牌爲:2♠ 2♣ K♣ Q♠ J♣ J♦ 9♥ 9♦ 8♠ 7♣ 7♦ 6♥ 5♠ 5♥ 4♥ 4♦ 3♠ 
    周星馳的牌爲:大王 小王 A♠ K♥ Q♣ Q♦ J♥ 10♣ 10♦ 9♠ 8♣ 7♥ 6♠ 6♦ 5♦ 4♠ 3♦ 
    底牌的牌爲:10♥ 9♣ 3♣
相關文章
相關標籤/搜索