集合 (二) ----- Map集合详解

相关文章

  1. 《集合 (一) ----- 集合的基本概念与Collection集合详解》
  2. 《集合 (二) ----- Map集合详解》



Map集合详解

一、Map集合基本概念
  1. Map集合的继承结构图

  2. Map集合的特点及其实现类

    特点
    (1) Map集合以key和value键值对的方式存储元素
    (2) 存储在key中的元素的特点是存取无序,且不能重复存储相同元素

    实现类
    (1) HashMap
    (2) Hashtable
    (3) Properties

  3. Map集合的子接口 ----- SortedMap集合特点及其实现类

    特点
    (1) 以key和value键值对的方式存储元素
    (2) 存储在key中的元素的特点是存取无序,且不能重复存储相同元素
    (3) 集合key部分元素会自动按照大小顺序排序

    实现类
    TreeMap

二、Map集合常用方法
返回值方法说明
Vput(K key, V value)存放键值对
Vget(Object key)通过key获取value
Vremove(Object key)通过key删除键值对
intsize()获取集合大小
voidclear()清空集合
booleanisEmpty()判空
booleancontainsKey(Object key)判断集合中是否包含某个key
booleancontainsValue(Object value)判断集合中是否包含某个value
Set < K >keySet()把Map集合所有key存放到Set集合中
Collection < V >values()把Map集合所有value存放到Collection集合中
Set<Map.Entry<K,V>>entrySet()将Map集合转为Set集合

注意:
当Map集合对象调用了entrySet()方法后,其每一个键值对会合并为一个元素(key=value)保存在Set集合中,其中Set集合中的元素数据类型为Map.Entry<K,V>,其中该类也提供了下面一些方法可以对键值进行获取、修改。

返回值方法说明
KgetKey()获取key=value中的key
VgetValue()获取key=value中的value
VsetValue(V value)修改key=value中的value

Map<Integer, String> map = new HashMap<>();

map.put(1, "YuKee");
map.put(2, "banana");
map.put(3, "DwD");

Set<Map.Entry<Integer, String>> set = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator1 = set.iterator();

while(iterator1.hasNext()) {
    System.out.println(iterator1.next());
}

Iterator<Map.Entry<Integer, String>> iterator2 = set.iterator();
while(iterator2.hasNext()) {
    System.out.println(iterator2.next().getValue());
}

        /* 
         * 输出:
         * 1=YuKee
         * 2=banana
         * 3=DwD
         * YuKee
         * banana
         * DwD
         * */

三、Map集合的两种遍历方式
  1. 通过key的Set集合来遍历

    
    Map<Integer, String> map = new HashMap<>();
    
    map.put(1, "kl");
    map.put(2, "dwd");
    map.put(3, "moerdan");
    map.put(4, "blue");
    
    Set<Integer> set = map.keySet();
    
    for (Integer i : set) {
        System.out.println(i + "-->" + map.get(i));
    }
            /*      
            输出:
            1-->kl
            2-->dwd
            3-->moerdan
            4-->blue
            */
            
    
  2. 将Map集合转换成Set集合,遍历Set集合(效率更高)

    
    Map<Integer, String> map = new HashMap<>();
    
    map.put(1, "YuKee");
    map.put(2, "banana");
    map.put(3, "DwD");
    
    Set<Map.Entry<Integer, String>> set = map.entrySet();
    
    for (Map.Entry<Integer, String> integerStringEntry : set) {
        System.out.println(integerStringEntry);
    }
    
            /*
             * 输出:
             * 1=YuKee
             * 2=banana
             * 3=DwD
             * */
             
    
四、HashMap集合详解
  1. 关于哈希表数据结构

    (1) 哈希表是数组和单向链表的结合体,其底层是一个一维数组,数组中的每一个元素又是一个单向链表。

    (2) 哈希表同时具有数组和单向链表的优点:查询效率和随机增删元素效率均较高。

  2. HashMap底层就是一个哈希表,其链表部分中的每一个节点(HashMap中的元素)具有以下属性
    (1) int hash,哈希值
    (2) K key,HashMap中的key
    (3) V value,HashMap中的value
    (4) Node<K, V> next,保存下一个节点的地址

  3. HashMap中put(K key, V value)方法底层原理

    1. 先将key,value封装到Node对象中。
    2. 底层会调用key的hashCode()方法得出hash值,然后通过哈希函数/算法,将哈希值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。
    3. 如果下标对应的位置上有链表,此时会拿着Node对象的key和链表上的每一个节点的key进行equals()比较,如果所有的equals()方法返回false,那么这个新节点将被添加到链表的末尾,如果其中有一个equals()方法返回了true,那么这个节点的value会被新节点的value覆盖。
  4. HashMap中的get(K key)方法底层原理

    1. 先调用key的hashCode()方法得出hash值,然后通过哈希函数/算法,将哈希值转换成数组的下标,通过这个下标快速定位到数组的对应下标位置。
    2. 如果这个位置上没有链表,则返回null,反之则会拿着这个传入的key和链表上的每一个节点上的key进行equals()比较。
    3. 如果所有equals方法返回false,则get方法返回null,如果有一个equals方法返回true,则get方法就返回对应节点的value。
  5. 注意

    (1) HashMap的key部分可以为null。

    (2) 同一个链表上的每一个节点(元素)hash值相同,因为其对应的数组下标相同。

    (3) 我们在使用HashMap集合对元素进行增删查找时,会先后调用元素的hashCode()equals()方法,所以对于自定义的数据类型使用HashMap存储时,我们必须要重写其hashCode()equals()方法。

    (4) HashMap的key部分存取元素无序的原因:不同元素调用hashCode()方法转换成的数组下标无规律,所以这些元素是随机挂在了对应的链表上。

    (5) HashMap的key部分元素不可重复的原因:元素中的equals()方法保证了不可重复,只要返回true则会覆盖掉原来的value值。

    (6) HashSet本质上就是HashMap的key部分,因此其存取元素无序,存储元素不可重复的原因和上面相同,而且对应HashSet集合中存储的自定义数据类型也需要重写hashCode()equals()方法。

    (7) 假设将待存取的元素的hashCode()方法返回值设置为固定值,那么所有元素都只会存储在哈希表底层下数组的同一个下标位置上,这样这个哈希表就变成了一个单向链表,无法发挥其作用了,想要发挥哈希表的作用,就必须让存取的元素散列均匀分布在底层数组的不同位置上,这也是我们需要重写自定义数据类型的hashCode()方法的原因。同理,如果让hashCode()方法返回值全都不同,那么哈希表就变成了一个一维数组。

    (8) 优化:HashMap初始化容量必须是2的倍数(默认16),有利于让存储的元素散列分布均匀,提高存取效率。

五、重写自定义数据类型的hashCode()和equals()方法

使用IDEA,想让哪几个属性作为判断元素的依据则在生成方法时勾选上即可


public class Student {
    private int num;
    private String name;

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

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return num == student.num &&
                name.equals(student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(num, name);
    }
}

结论:存放在HashMap的key部分的元素,以及存放在HashSet中的元素,需要同时重写hashCode()equals()方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值