广告位联系
返回顶部
分享到

Java之HashMap的介绍

java 来源:转载 作者:秩名 发布时间:2021-08-11 07:49:15 人浏览
摘要

这篇文章,我们打算探索一下Java集合(Collections)框架中Map接口中HashMap的实现。Map虽然是Collctions框架的一部分,但是Map并没有实现Collection接口,而Set和List是实现Collection接口的。 简单来说,HashMap主要通过key存储value值,并且提供了添加,获

这篇文章,我们打算探索一下Java集合(Collections)框架中Map接口中HashMap的实现。Map虽然是Collctions框架的一部分,但是Map并没有实现Collection接口,而Set和List是实现Collection接口的。

简单来说,HashMap主要通过key存储value值,并且提供了添加,获取和操作存储value的方法。HashMap的实现基于HashTable。


HashMap内部呈现

Key-value对在内部是以buckets的方式存储在一起,最终成为一个表。存储和检索操作的时间是固定的,也就是时间复杂度为O(1)。

这篇文章暂时不过于涉及HashMap的底层,我们先对HashMap有个整体认知。

put方法

Map中通过put方法来存储一个value。

/**
 * 建立键值对应关系,如果之前已经存在对应的key,
 * 返回之前存储的value,之前如果不存在对应的key,返回null
 */
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

知识点一: 当Map的调用put方法的时候,key对象被调用hashCode()方法,获得一个hash值供hashmap使用。
我们创建一个对象来证实一下。

public class MyKey {
    private int id;
    
    @Override
    public int hashCode() {
        System.out.println("调用 hashCode()");
        return id;
    }

    // constructor, setters and getters
}

   @Test
    public void mapKeyTest(){
        HashMap<MyKey,String> map = new HashMap<MyKey, String>();
        String retV = map.put(new MyKey(1),"value1");
    }

可以看到控制台的输出信息

调用 hashCode()

知识点二: hash()方法计算出的hash值可以标识它在buckets数组中的索引位置。

HashMap的hash()方法如下:可以与put方法进行关联。

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

HashMap有一个特点,它可以存储null的key和null的value。当key时null的时候,执行put方法,它会自动分配hash为0. 这也意味着key为null的时候没有hash操作,这样就避免了空指针异常。

get() 方法

为了获取存储在hashMap中的对象,我们需要知道与它对应的key。然后通过get方法把对应的key传到参数里。调用HashMap的get方法的时候,也会调用key对象的hashCode方法。

@Test
public void mapKeyTest(){
    HashMap<MyKey,String> map = new HashMap<MyKey, String>();
    MyKey key1 = new MyKey(1);
    map.put(key1,"value1");
    String retV =  map.get(key1);
}

控制台上可以看到两行输出

 

调用 hashCode()
调用 hashCode()

HashMap中的集合视图

HashMap提供了三种方式,让我们可以把key和value作为其它集合来使用。

Set<K> keys = map.keySet()
Collection<V> values = map.values()
Set<Entry<K, V>> entries = map.entrySet();

注意: 在iteators创建完毕后,对map的任何结构修改,都会抛出一个异常。

@Test
public void givenIterator_whenFailsFastOnModification_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("name", "baeldung");
    map.put("type", "blog");
 
    Set<String> keys = map.keySet();
    Iterator<String> it = keys.iterator();
    map.remove("type");
    while (it.hasNext()) {
        String key = it.next();
    }
}

// 会抛出java.util.ConcurrentModificationException异常

HashMap中唯一允许的修改是在iterator中移除元素。

public void givenIterator_whenRemoveWorks_thenCorrect() {
    Map<String, String> map = new HashMap<>();
    map.put("name", "baeldung");
    map.put("type", "blog");
 
    Set<String> keys = map.keySet();
    Iterator<String> it = keys.iterator();
 
    while (it.hasNext()) {
        it.next();
        it.remove();
    }
 
    assertEquals(0, map.size());
}

HashMap在iterator上的性能相比于LinkedHashMap和treeMap,性能非常糟糕。最差情况下为O(n),n为hashmap中条目的个数。

HashMap性能

HashMap的性能主要有两个参数影响,初始容量和负载因子。初始容量为Map底层桶数组的长度,负载因子为当桶容量的长度为多大的时候,重新开辟新的空间。

int threshold;
final float loadFactor;

默认的初始容量为16,默认的负载因子为0.75. 我们也可以自定义它们的值。

Map<String,String> hashMapWithCapacity=new HashMap<>(32);
Map<String,String> hashMapWithCapacityAndLF=new HashMap<>(32, 0.5f);

初始容量:

大的初始容量用于条目数较多,但是少量迭代(iteration)
小的初始容量用于条目数较少,但是多次迭代(iteration)

负载因子:
0.75是一个很折衷的方案了。在我们初始化HashMap的时候,初始容量和负载因子都应该考虑在内,比如为了减少重新hash的操作,初始容量乘以负载因子应该大于能存储的最大条目数,这样就不会发生重新hash的操作。

最后

HashMap内部有很多东西值得探索,这篇仅仅对HashMap做了一层表面的分析。接下来会深入分析。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://www.jianshu.com/p/be9ffb76db30
相关文章
  • SpringBoot自定义错误处理逻辑介绍

    SpringBoot自定义错误处理逻辑介绍
    1. 自定义错误页面 将自定义错误页面放在 templates 的 error 文件夹下,SpringBoot 精确匹配错误信息,使用 4xx.html 或者 5xx.html 页面可以打印错误
  • Java实现手写一个线程池的代码

    Java实现手写一个线程池的代码
    线程池技术想必大家都不陌生把,相信在平时的工作中没有少用,而且这也是面试频率非常高的一个知识点,那么大家知道它的实现原理和
  • Java实现断点续传功能的代码

    Java实现断点续传功能的代码
    题目实现:网络资源的断点续传功能。 二、解题思路 获取要下载的资源网址 显示网络资源的大小 上次读取到的字节位置以及未读取的字节
  • 你可知HashMap为什么是线程不安全的
    HashMap 的线程不安全 HashMap 的线程不安全主要体现在下面两个方面 在 jdk 1.7 中,当并发执行扩容操作时会造成环形链和数据丢失的情况 在
  • ArrayList的动态扩容机制的介绍

    ArrayList的动态扩容机制的介绍
    对于 ArrayList 的动态扩容机制想必大家都听说过,之前的文章中也谈到过,不过由于时间久远,早已忘却。 所以利用这篇文章做做笔记,加
  • JVM基础之字节码的增强技术介绍

    JVM基础之字节码的增强技术介绍
    字节码增强技术 在上文中,着重介绍了字节码的结构,这为我们了解字节码增强技术的实现打下了基础。字节码增强技术就是一类对现有字
  • Java中的字节码增强技术

    Java中的字节码增强技术
    1.字节码增强技术 字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。 参考地址 2.常见技术 技术分类 类
  • Redis BloomFilter布隆过滤器原理与实现

    Redis BloomFilter布隆过滤器原理与实现
    Bloom Filter 概念 布隆过滤器(英语:Bloom Filter)是1970年由一个叫布隆的小伙子提出的。它实际上是一个很长的二进制向量和一系列随机映射
  • Java C++算法题解leetcode801使序列递增的最小交换次

    Java C++算法题解leetcode801使序列递增的最小交换次
    题目要求 思路:状态机DP 实现一:状态机 Java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int minSwap(int[] nums1, int[] nums2) { int n
  • Mybatis结果集映射与生命周期介绍

    Mybatis结果集映射与生命周期介绍
    一、ResultMap结果集映射 1、设计思想 对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了 2、resultMap的应用场
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计