热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java源码HashMap

HashMap类https:docs.oracle.comjavase8docsapijavautilHashMap.htmlpublicclassHa

 HashMap类

  https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html

  

  public class HashMap extends AbstractMap implements Map, Cloneable, Serializable

  

  子类:LinkedHashMap,PrinterStateReasons

 

  一、介绍

  HashMap继承了AbstractMap类,AbstractMap类实现Map接口中的一些方法,是为了最大限度地减少实现Map接口需要进行的工作量,提高代码重用。  

  Cloneable接口可以理解为一个标记接口,它并没有定义方法。当一个对象调用Object的clone方法时,如果这个对象没有实现Cloneable接口,会引发CloneNotSupportedException异常。(关于对象的拷贝,有深拷贝和浅拷贝的知识)clone方法执行的是浅拷贝,这个接口就是指示可以使用Object的clone方法。

  

  HashMap类实现了Map接口中所有的“可选操作”,允许key和value为null,非同步。

  HashTable类与HashMap几乎一致,区别在于HashTable不允许key和value为null,而且是同步的。

  HashMap无法保证其中键值对的顺序,即它无法保证键值对的顺序会一直保持不变。

 

  HashMap类为基本的get和put操作,提供了常数时间的性能。

  HashMap的集合视图上的迭代器所需要的时间和(HashMap实例的容量加上键值对的数量)成正比。所以,当需要考虑迭代器的性能时,不要把HashMap实例的容量(capacity属性)设置得太高,或者别把load factor设置得太低。

  如上提到的,一个HashMap实例有两个最重要的参数影响着它的性能:初始的capacity值和load factor值。

  capacity是哈希表中桶的数量,它的初始值代表着HashMap实例创建时桶的数量。

  load factor,在这个哈希表实例进行容量自动增长前,用来度量这个哈希表“满”的程度。

  当哈希表中中键值对条目的数量超过load-factor和当前capacity的乘积时,哈希表就会被重新映射(is rehashed),这意味着它内部的数据结构被重建了,结果便是“新”的哈希表拥有了两倍的桶数。(像数组的自动扩容)

 

  通常,HashMap的默认load-factor是0.75,这在时间代价和空间代价提供了一个很好的平衡。

  load-factor过高会减少空间开销,但会增加查找成本,这体现在类中的大部分操作中,包括get和put。

  因此,在设置初始的capacity值时,需要好好考虑HashMap中键值条目的数量以及负载因子,以尽可能减少重新散列的次数。如果初始capacity大于(最大入口数除以负载因子),哈希表就不会进行重新散列。

  

  如果你有比较多的键值对需要存进HashMap,最好在创建它时配置一个足够大的capacity,比起容量不足然后进行重散列,这样可以更有效地存储映射。

  需要注意的是,如果多个key的hashcode()值相同,会降低哈希表的性能。为了改善这种情况,若key实现了Comparable接口,HashMap可能会利用key之间的比较顺序(comparison order)来消除“并列”(break ties)。

 

  由于HashMap是不同步的,当多个线程并发访问一个HashMap时,若其中至少一个线程对HashMap进行了结构性的修改,那么就必须在外部进行同步(it must be synchronized externally)。这通常是通过自然封装了HashMap的对象上完成的,如果这样的对象不存在,那么这个HashMap就应该通过Collections.synchronizedMap方法装饰,并且最好在创建时就进行,以防止这个HashMap上有以外的非同步访问:

Map m = Collections.synchronizedMap(new HashMap(...));

  (所谓的结构性修改(structural modification)是增加or删除键值记录的操作,如果仅仅是改变其中某个key关联的value,不属于结构性操作。)

 

  HashMap类的所有集合视图的产生方法所返回的迭代器都具有“快速失效”属性:当一个实例在迭代器被创建后被进行了结构性的修改(除了通过迭代器的remove方法),迭代器会抛出一个 ConcurrentModificationException。所以,面对并发修改,迭代器会明确而快速地失效,而不是仍然承担着并不确定的行为的风险。

  需要注意的是,迭代器并不保证它的“快速失效”属性,因为在存在非同步并发修改的情形下,不可能做出任何硬性保证。

  所以,迭代器的快速失效行为应该仅用于检测错误。

 

  二、HashMap的嵌套类

  继承自AbstractMap的嵌套类:AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry

  继承自Map接口的嵌套类:Map.Entry

  事实是,HashMap中有许多嵌套类:

  如图:

  Node类实现了Map.Entry接口,源码描述为“Basic hash bin node, used for most entries”,即为存储单条键值映射记录的数据结构;

  TreeNode类是“Entry for Tree bins”,这个是在某些情况下可以用来替代Node的(when bins get too large, they are transformed into bins of TreeNodes),该类继承自LinkedHashMap.Entry

  KeySet、Values、EntrySet的嵌套类和返回集合视图的三个方法相关;

 

  本来不打算解析嵌套类的,但是它们和很多方法息息相关,所以干脆好好分析一波!

  1、HashMap的属性

 

  如图,基本可以通过名称判断相关属性的作用。

  size,表示当前map中键值映射记录的条数;

  带treeify字段的属性,好像是和Node转换成TreeNode的操作有关的;

  threshold意思是门槛、开始,用于判断map是否进行resize操作的标准。

  table,一个包含Node元素的数组,会在第一次使用时被初始化。这个数组的长度,总是整数2的幂。

  modCount,记录这个map被结构性修改的次数;

 

  2、 static class Node implements Map.Entry 

  注意到,其中key为final,但是value不为final,关注下put方法中包含相同key时的处理方法。

  这里的hash暂时没明白干嘛的;

  next是不是表明了,键值映射记录的存储方式是链表?

/**
     * Basic hash bin node, used for most entries.  (See below for
     * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
     */
    static class Node implements Map.Entry {
        final int hash;
        final K key;
        V value;
        Node next;

        Node(int hash, K key, V value, Node next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry e = (Map.Entry)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

 

  三、构造器

  根据Java集合框架的规约,集合类都应该提供一个无参构造器和一个Collection or Map类型的单参构造器,前者用于构造空的集合,后者用于复制另一个集合。

  •   HashMap(),构造一个空的HashMap实例,默认capacity为16,默认load-factor是0.75。
  •   HashMap(Map m),这里的初始capacity,满足需求即可。
  •   HashMap(int initialCapacity)
  •   HashMap(int initialCapacity, float loadFactor)

 

  四、方法

  1、public int size()

  返回键map中值映射记录的条数;

 

  2、public boolean isEmpty()

  如果map中还没有存储键值映射的记录,返回true。

public boolean isEmpty() {
        return size == 0;
    }

 

  3、static final int hash(Object key)

  待会看看它的用法。

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

 

 

  4、public V get(Object key)

  返回指定key所映射的value值;若map不包含此key的映射,返回null。

  特殊的情况是,这个key所映射的value值恰好为null,这时就需要利用containKey()来区分这两种情况了。

  

  5、public boolean containsKey(Object key)

  查询map中是否包含指定的key

 

  6、public V put(K key, V value)

  关联指定的key和value,即是他们称为map中的键值对,可以通过key查到这个value。

  如果key存在,对value进行替换。

 

  7、public void putAll(Map m)

  将指定map中的所有映射复制到此map。

  如果key已存在,则其值会被新的替换。

 

  8、public V remove(Object key)

  9、public void clear()

  清空map

 

  10、public boolean containsValue(Object value)

  判断是否map中有一个或者多个key的映射值为指定的value。

 

  11、public Set keySet()

  Returns a Set view of the keys contained in this map.

 

  12、public Collection values()

  Returns a Collection view of the values contained in this map.

 

  13、public Set> entrySet()

  Returns a Set view of the mappings contained in this map

 

  14、public V putIfAbsent(K key, V value)

  If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.

 

  15、public boolean remove(Object key, Object value)

  

  16、public boolean replace(K key, V oldValue, V newValue)

  Replaces the entry for the specified key only if currently mapped to the specified value.

 


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文介绍了GregorianCalendar类的基本信息,包括它是Calendar的子类,提供了世界上大多数国家使用的标准日历系统。默认情况下,它对应格里高利日历创立时的日期,但可以通过调用setGregorianChange()方法来更改起始日期。同时,文中还提到了GregorianCalendar类为每个日历字段使用的默认值。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
author-avatar
小晴天9927
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有