Java之HashMap案例详解

 更新时间:2021年08月10日 16:44:04   作者:Real_man  
这篇文章主要介绍了Java之HashMap案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

概述

这篇文章,我们打算探索一下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做了一层表面的分析。接下来会深入分析。

到此这篇关于Java之HashMap案例详解的文章就介绍到这了,更多相关Java之HashMap内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Java父子类加载顺序

    浅谈Java父子类加载顺序

    本文主要介绍了Java父子类加载顺序,那么这么长怎么记呀?本文就帮大家总结一下,更有利于记忆,感兴趣的可以了解一下
    2021-08-08
  • Java简单从文件读取和输出的实例

    Java简单从文件读取和输出的实例

    下面小编就为大家带来一篇Java简单从文件读取和输出的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • 简单了解Java中的可重入锁

    简单了解Java中的可重入锁

    这篇文章主要介绍了简单了解Java中的可重入锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • java的Guava工具包介绍

    java的Guava工具包介绍

    Java开发的同学应该都使用或者听说过Google提供的Guava工具包。日常使用最多的肯定是集合相关的工具类,还有Guava cache,除了这些之外Guava还提供了很多有用的功能,鉴于日常想用的时候找不到,这里就梳理一下Guava中那些好用的工具类,想优化代码的时候不妨过来看看
    2021-04-04
  • 解决Spring Security集成knife4j访问接口文档出现403的问题

    解决Spring Security集成knife4j访问接口文档出现403的问题

    这篇文章主要给大家介绍了如何解决Spring Security集成knife4j访问接口文档出现403的问题,文中有详细的解决方案,有需要的朋友可以参考阅读下
    2023-07-07
  • SpringBoot整合Guava Cache实现全局缓存的示例代码

    SpringBoot整合Guava Cache实现全局缓存的示例代码

    这篇文章主要介绍了SpringBoot整合Guava Cache实现全局缓存,Guava Cache是Google Guava库中的一个模块,提供了基于内存的本地缓存实现,文中介绍了SpringBoot整合使用Guava Cache的具体步骤,需要的朋友可以参考下
    2024-03-03
  • Java之Maven工程打包jar

    Java之Maven工程打包jar

    Maven打包一般可以生成两种包一种是可以直接运行的包,一种是依赖包(只是编译包)。Maven默认打包时jar,如果需要修改其他类型,可以修改pom.xml。感兴趣的同学可以参考阅读
    2023-04-04
  • Java+Selenium实现文件上传下载功能详解

    Java+Selenium实现文件上传下载功能详解

    这篇文章主要介绍了java代码如何利用selenium操作浏览器上传和下载文件功能,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-01-01
  • 基于Failed to load ApplicationContext异常的解决思路

    基于Failed to load ApplicationContext异常的解决思路

    这篇文章主要介绍了基于Failed to load ApplicationContext异常的解决思路,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 一篇文章带了解如何用SpringBoot在RequestBody中优雅的使用枚举参数

    一篇文章带了解如何用SpringBoot在RequestBody中优雅的使用枚举参数

    这篇文章主要介绍了SpringBoot中RequestBodyAdvice使用枚举参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08

最新评论