`
ssydxa219
  • 浏览: 622461 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

HashMap

 
阅读更多

 

 

Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}

 

 

Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}

 

 

 

static final int DEFAULT_INITIAL_CAPACITY = 16;

最大容量2的15次方+1;
static final int MAXIMUM_CAPACITY = 1 << 30;

默认加载因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;

内部实现的机制是用具有键值对格式的单个的entry数组实现:

  1. transient Entry[] table;
  2.     static   class  Entry<K,V> implements Map.Entry<K,V> {
  3.         final K key;  //键
  4.         V value;     // 值
  5.         Entry<K,V> next;   //下一个
  6.         final  int  hash;    //哈西值
  7.          /**
  8.          * Creates new entry.
  9.          */
  10.         Entry(int  h, K k, V v, Entry<K,V> n) {
  11.             value = v;
  12.             next = n;
  13.             key = k;
  14.             hash = h;
  15.         }
  16.          public  final K getKey() {
  17.              return  key;
  18.         }
  19.          public  final V getValue() {
  20.              return  value;
  21.         }
  22.          public  final V setValue(V newValue) {
  23.         V oldValue = value;
  24.             value = newValue;
  25.              return  oldValue;
  26.         }
  27.          public  final boolean equals(Object o) {
  28.              if  (!(o instanceof Map.Entry))
  29.                  return   false ;
  30.             Map.Entry e = (Map.Entry)o;
  31.             Object k1 = getKey();
  32.             Object k2 = e.getKey();
  33.              if  (k1 == k2 || (k1 !=  null  && k1.equals(k2))) {
  34.                 Object v1 = getValue();
  35.                 Object v2 = e.getValue();
  36.                  if  (v1 == v2 || (v1 !=  null  && v1.equals(v2)))
  37.                      return   true ;
  38.             }
  39.              return   false ;
  40.         }
  41.          public  final  int  hashCode() {
  42.              return  (key== null    ? 0 : key.hashCode()) ^
  43.                    (value== null  ? 0 : value.hashCode());
  44.         }
  45.          public  final String toString() {
  46.              return  getKey() +  "="  + getValue();
  47.         }
  48.          void  recordAccess(HashMap<K,V> m) {
  49.         }
  50.          void  recordRemoval(HashMap<K,V> m) {
  51.         }
  52.     }

当前的数组大小:
transient int size;

构造函数初始化:
设置初始化的容积和加载因子

  1. public  HashMap( int  initialCapacity,  float  loadFactor) {
  2.          //初始化容积必须大于0
  3.          if  (initialCapacity < 0)
  4.              throw   new  IllegalArgumentException( "Illegal initial capacity: "  +
  5.                                                initialCapacity);
  6.          //超过最大容积的时候,那么等于最大容积
  7.          if  (initialCapacity > MAXIMUM_CAPACITY)
  8.             initialCapacity = MAXIMUM_CAPACITY;
  9.          //初始化加载因子;
  10.          if  (loadFactor <= 0 || Float.isNaN(loadFactor))
  11.              throw   new  IllegalArgumentException( "Illegal load factor: "  +
  12.                                                loadFactor);
  13.          //找出一个数的平方大于当前给出的初始化容积,比如是初始化是10的话,那么最终的容积就是16
  14.          int  capacity = 1;
  15.          while  (capacity < initialCapacity)
  16.             capacity <<= 1;
  17.          this .loadFactor = loadFactor;
  18.         threshold = ( int )(capacity * loadFactor);
  19.          //用容积来初始化数组大小;size当前还是为0;
  20.         table =  new  Entry[capacity];
  21.          //初始化动作,可以留给子类实现,模板模式的应用
  22.         init();
  23.     }



 这是一个hashcode的转换算法;他能够把对象的hashcode转换成为小于length-1的整数作为数组的下标;那么势必会出现重合

  1.      static   int  hash( int  h) {
  2.         h ^= (h >>> 20) ^ (h >>> 12);
  3.          return  h ^ (h >>> 7) ^ (h >>> 4);
  4.     }
  5.      static   int  indexFor( int  h,  int  length) {
  6.          return  h & (length-1);
  7.     }



我们先看增加方法:

  1.   public  V put(K key, V value) {
  2.          //判断键值是否为空;
  3.          if  (key ==  null )
  4.              return  putForNullKey(value);
  5.         
  6.          //得到hashcode
  7.          int  hash = hash(key.hashCode());
  8.          //得到一个小于数组长度(取的是与操作)的下标
  9.          int  i = indexFor(hash, table.length);
  10.          //在数组中找到该下标的entry值;
  11.          //事实上entry也是一个链表,相对于LinkedList来说,他的entry是单向的
  12.          for  (Entry<K,V> e = table[i]; e !=  null ; e = e.next) {
  13.             Object k;
  14.              //如果存在键值是同一对象的entry,那么用新值覆盖旧值,不存在则再往下找,知道末尾
  15.              if  (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  16.                 V oldValue = e.value;
  17.                 e.value = value;
  18.                 e.recordAccess( this );
  19.                  return  oldValue;
  20.             }
  21.         }
  22.          //增加修改次数
  23.         modCount++;
  24.          //增加这个entry到该下标列表的首部
  25.         addEntry(hash, key, value, i);
  26.          return   null ;
  27.     }
  28.      void  addEntry( int  hash, K key, V value,  int  bucketIndex) {
  29.     Entry<K,V> e = table[bucketIndex];
  30.          //可见是放在該下标链表的第一位的
  31.         table[bucketIndex] =  new  Entry<K,V>(hash, key, value, e);
  32.          //在这里增加了size;
  33.          if  (size++ >= threshold)
  34.             resize(2 * table.length);
  35.     }



如果key是null的话:
 那么他可以不用通过hashcode定位数组中的队列下标-_-||事实上他也没有hashcode;规定在0位存放这个链表的头;可见在HashMap中是可以

存放null的key的;但是正因为其没有hashcode那么就只能存放一个元素,而不是像其他一样能存放多个;但是另外我们可见,你可以考虑把使

用的最多的值的键设置成为null,因为他是找到的最快的;

  1. private  V putForNullKey(V value) {
  2.          for  (Entry<K,V> e = table[0]; e !=  null ; e = e.next) {
  3.              if  (e.key ==  null ) {
  4.                 V oldValue = e.value;
  5.                 e.value = value;
  6.                 e.recordAccess( this );
  7.                  return  oldValue;
  8.             }
  9.         }
  10.         modCount++;
  11.         addEntry(0,  null , value, 0);
  12.          return   null ;
  13.     }


上面说了存放了,下面我们再来看看如何取出来把:

  1. final Entry<K,V> getEntry(Object key) {
  2.          //经过算法得到这个key对应的hashcode,可见hashcode是固定的对应,而不是随机的;如果是null的话为0,经过与操作还是0,直接 
  3.         //定位到table[0]否则查找出下标
  4.         int  hash = (key ==  null ) ? 0 : hash(key.hashCode());
  5.          for  (Entry<K,V> e = table[indexFor(hash, table.length)];
  6.              e !=  null ;
  7.              e = e.next) {
  8.             Object k;
  9.       //匹配这个key的hashcode和数值,可见不是同一的值其hashcode是可能一样的,否则如果是一一对应则没必要匹配key了
  10.              if  (e.hash == hash &&
  11.                 ((k = e.key) == key || (key !=  null  && key.equals(k))))
  12.                  return  e;
  13.         } 
  14.      //没有找到,为空;
  15.         return   null ;


我们再来看看其容量是如何增长的:

  1.      public   void  putAll(Map<? extends K, ? extends V> m) {
  2.          //得到新增加进来的元素的个数
  3.          int  numKeysToBeAdded = m.size();
  4.          if  (numKeysToBeAdded == 0)
  5.              return ;
  6.           //如果新增加的元素个数大于原来容积*加载因子;可见我们必须要扩充容量了
  7.          if  (numKeysToBeAdded > threshold) {
  8.              //那么我们增加的容量将是该新增元素个数+预留空间的总数
  9.              int  targetCapacity = ( int )(numKeysToBeAdded / loadFactor + 1);
  10.              if  (targetCapacity > MAXIMUM_CAPACITY)
  11.                 targetCapacity = MAXIMUM_CAPACITY;
  12.              int  newCapacity = table.length;
  13.              //如果我们现在的表的容量小于新增加的容量,那么我们扩展两倍,如果还小,再扩大两倍,直到他的大于当前需要的容量
  14.              while  (newCapacity < targetCapacity)
  15.                 newCapacity <<= 1;
  16.              if  (newCapacity > table.length)
  17.                 resize(newCapacity);
  18.         }
  19.         
  20.          //添置新的"家具"
  21.          for  (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
  22.             Map.Entry<? extends K, ? extends V> e = i.next();
  23.             put(e.getKey(), e.getValue());
  24.         }
  25.     }

用新的容积开辟了一块足够大的存储空间,

  1. void  resize( int  newCapacity) {
  2.         Entry[] oldTable = table;
  3.          int  oldCapacity = oldTable.length;
  4.          if  (oldCapacity == MAXIMUM_CAPACITY) {
  5.             threshold = Integer.MAX_VALUE;
  6.              return ;
  7.         }
  8.         Entry[] newTable =  new  Entry[newCapacity];
  9.         transfer(newTable);
  10.         table = newTable;
  11.         threshold = ( int )(newCapacity * loadFactor);
  12.     }
  13. 并且把家具从以前的“小房子”帮到了“大房子”
  14.   void  transfer(Entry[] newTable) {
  15.         Entry[] src = table;
  16.          int  newCapacity = newTable.length;
  17.          for  ( int  j = 0; j < src.length; j++) {
  18.              //得到各个数组元素的链表的头
  19.             Entry<K,V> e = src[j];
  20.              if  (e !=  null ) {
  21.                 src[j] =  null //一定要把原来的“小房子”收拾好,方便垃圾回收;
  22.                  do  {
  23.                     Entry<K,V> next = e.next;
  24.                      int  i = indexFor(e.hash, newCapacity);
  25.                     e.next = newTable[i];
  26.                     newTable[i] = e;
  27.                     e = next;
  28.                 }  while  (e !=  null );
  29.             }
  30.         }
  31.     }


新增,查找,都看过了,我们来看一下删除:

  1. public  V remove(Object key) {
  2.         Entry<K,V> e = removeEntryForKey(key);
  3.          return  (e ==  null  ?  null  : e.value);
  4.     }
  5.   
  6.     final Entry<K,V> removeEntryForKey(Object key) {
  7.          //定位查找到链表表头;
  8.          int  hash = (key ==  null ) ? 0 : hash(key.hashCode());
  9.          int  i = indexFor(hash, table.length);
  10.         Entry<K,V> prev = table[i];
  11.         Entry<K,V> e = prev;
  12.          while  (e !=  null ) {
  13.             Entry<K,V> next = e.next;
  14.             Object k;
  15.              if  (e.hash == hash &&
  16.                 ((k = e.key) == key || (key !=  null  && key.equals(k)))) {
  17.                 modCount++;
  18.                  //若能找到,修改前后的链表节点的指向,从中删除;
  19.                 size--;
  20.                  if  (prev == e)
  21.                     table[i] = next;
  22.                  else
  23.                     prev.next = next;
  24.                 e.recordRemoval( this );
  25.                  return  e;
  26.             }
  27.              //方便链表往下传递;保存prev是因为是单向链表,所以一旦找到的目标节点,无法通过该节点得到钱一个节点,就无法更改前
  28. 一个节点的指 //向,所以要保存prev
  29.             prev = e;
  30.             e = next;
  31.         }
  32.          return  e;
  33.     }



现在我们再看一下如何单独取到他的键值集合或者值集合:

  1. values实现了一个内部私有类;
  2.    public  Collection<V> values() {
  3.         Collection<V> vs = values;
  4.          return  (vs !=  null  ? vs : (values =  new  Values()));
  5.     }
  6. //AbstractCollection<V> 把一些基本的curd委托给了iteartor,所以只需重载其获取iteartor的方法;
  7.      private  final  class  Values extends AbstractCollection<V> {
  8.         
  9.          //重载了iteartor
  10.          public  Iterator<V> iterator() {
  11.              return  newValueIterator();
  12.         }
  13.          public   int  size() {
  14.              return  size;
  15.         }
  16.          public  boolean contains(Object o) {
  17.              return  containsValue(o);
  18.         }
  19.          public   void  clear() {
  20.             HashMap. this .clear();
  21.         }
  22.     }
  23. 下面是该iteartor的获取:其中主要的是看看我们获取的value的collection是如何排序的;
  24.      private   abstract   class  HashIterator<E> implements Iterator<E> {
  25.         Entry<K,V> next;     // next entry to return
  26.          int  expectedModCount;    // For fast-fail
  27.          int  index;       // current slot
  28.         Entry<K,V> current;  // current entry
  29.         HashIterator() {
  30.             expectedModCount = modCount;
  31.              //如果该HashMap有元素
  32.              if  (size > 0) {  // advance to first entry
  33.                 Entry[] t = table;
  34.                 然后把next和index都调至该table数组的链表头中不为空的地方;
  35.                  while  (index < t.length && (next = t[index++]) ==  null )
  36.                     ;
  37.             }
  38.         }
  39.          public  final boolean hasNext() {
  40.              return  next !=  null ;
  41.         }
  42.         final Entry<K,V> nextEntry() {
  43.              if  (modCount != expectedModCount)
  44.                  throw   new  ConcurrentModificationException();
  45.             Entry<K,V> e = next;
  46.              if  (e ==  null )
  47.                  throw   new  NoSuchElementException();
  48.              //代表这个链表遍历完成了,需要开始遍历下一个table下标的链表了
  49.              if  ((next = e.next) ==  null ) {
  50.                 Entry[] t = table;
  51.              //找到下一个不为空的table元素
  52.                  while  (index < t.length && (next = t[index++]) ==  null )
  53.                     ;
  54.             }
  55.         current = e;
  56.              return  e;
  57.         }
  58.          public   void  remove() {
  59.              if  (current ==  null )
  60.                  throw   new  IllegalStateException();
  61.              if  (modCount != expectedModCount)
  62.                  throw   new  ConcurrentModificationException();
  63.             Object k = current.key;
  64.             current =  null ;
  65.             HashMap. this .removeEntryForKey(k);
  66.             expectedModCount = modCount;
  67.         }
  68.     }
  69. ValueIterator :
  70.   private  final  class  ValueIterator extends HashIterator<V> {
  71.          public  V next() {
  72.              return  nextEntry().value;
  73.         }
  74.     }

这是我们会问加入我在这个value的collection中增加元素会出现什么样的情况呢,次序会不会打乱,对不起,AbstractCollection不存在add

的,iteartor也是不允许增加元素的;

对于来说keySet来说是同理的,不过因为key是不存在一样的,所以,我们不会返回collection而返回set,避免了重复key的出现;

分享到:
评论

相关推荐

    HashMap之resize()方法源码解读.docx

    HashMap之resize()方法源码解读 HashMap的resize()方法是HashMap中最核心的方法之一,该方法负责扩容HashMap的容量,以便存储更多的键值对。下面我们将对HashMap的resize()方法进行源码解读,了解其扩容机制和原理...

    hashmap面试题_hashmap_

    《HashMap面试题详解》 HashMap作为Java集合框架中的重要成员,是面试中常见的知识点,尤其在数据结构与算法、并发编程以及JVM内存管理等领域,HashMap的深入理解至关重要。本篇将围绕HashMap的相关面试题,从基础...

    hashMap和hashTable的区别

    ### hashMap和hashTable的区别 #### 一、简介与基本概念 `HashMap` 和 `HashTable` 都是 Java 集合框架中非常重要的数据结构,它们都实现了 `Map` 接口,用于存储键值对。尽管它们在功能上有很多相似之处,但在...

    关于如何解决HashMap线程安全问题的介绍

    在Java编程中,HashMap是一个非常常用的集合类,用于存储键值对数据。然而,它存在一个重要的特性,那就是线程不安全。理解这个问题并找到解决方案是每个Java开发者必须掌握的知识。 HashMap线程不安全的原因主要...

    HashMap和HashTable的区别和不同

    ### HashMap与HashTable的区别详解 #### 引言 在Java编程中,`HashMap`与`HashTable`作为两种常用的数据结构,经常被用来存储键值对数据。尽管它们在功能上相似,但在实现细节、性能表现以及使用场景方面存在显著...

    HashMap介绍和使用

    ### HashMap介绍和使用详解 #### 一、HashMap的数据结构 HashMap是Java集合框架的一个重要组成部分,它实现了Map接口,能够存储键值对映射。在Java编程语言中,最基本的数据结构有两种:数组和引用(模拟指针)。...

    JNI处理hashmap,string等对象的操作

    在这个主题中,我们将深入探讨如何使用JNI处理HashMap、String等对象。 首先,让我们来理解JNI的基本结构。JNI接口提供了大量的函数,让本地方法(C/C++代码)能够创建、访问和修改Java对象。要使用JNI,你需要定义...

    用HashMap模拟一个网上购物车

    ### 使用HashMap模拟网上购物车 #### 一、项目背景与目标 在本实验中,我们通过使用Java语言中的`HashMap`来模拟一个简单的网上购物车系统。该项目的主要目的是熟悉Java集合框架中的`HashMap`类,并了解如何利用它...

    HashMap的数据结构

    HashMap是Java编程语言中一个非常重要的数据结构,它属于集合框架的一部分,主要用于存储键值对(Key-Value)数据。HashMap在内部实现上基于哈希表,也称为散列表,它提供了一种快速查找、插入和删除数据的方法,...

    ibatis 用HashMap解决resultClass映射

    ### ibatis 使用 HashMap 解决 resultClass 映射 在日常的软件开发过程中,尤其是在处理数据库查询时,我们经常面临一个问题:如何优雅地处理那些未知或动态变化的列名及列数的情况?在这种情况下,传统的实体类...

    Java HashMap类详解

    Java HashMap 类详解 本资源详细介绍了 Java 中的 HashMap 类,包括其实现机制、Hash 存储机制、集合存储机制等方面的知识点。 1. HashMap 和 HashSet 的关系 HashMap 和 HashSet 是 Java Collection Framework ...

    Java中HashMap详解(通俗易懂).doc

    Java中的HashMap是一个非常重要的数据结构,它实现了Map接口,提供了键值对的高效存储和访问。HashMap基于哈希表(也称为散列表)原理工作,它允许用户通过键(Key)快速查找对应的值(Value)。在HashMap中,键和值...

    hashmap实现原理

    哈希映射(HashMap)是Java编程语言中广泛使用的数据结构之一,主要提供键值对的存储和查找功能。HashMap的实现基于哈希表的概念,它通过计算对象的哈希码来快速定位数据,从而实现了O(1)的平均时间复杂度。在深入...

    HashMap类.rar

    HashMap类在Java编程语言中是集合框架的一部分,它是一个基于哈希表的Map接口实现。HashMap提供了快速的插入、删除和查找操作,平均时间复杂度为O(1)。在这个压缩包“HashMap类.rar”中,包含的是易语言版本的...

    java中HashMap,LinkedHashMap,TreeMap,HashTable的区别

    ### Java中HashMap, LinkedHashMap, TreeMap,HashTable的区别 在Java编程语言中,`Map`接口是集合框架中的一个重要组成部分,用于存储键值对。本文将详细分析四种常用的`Map`实现类:`HashMap`, `LinkedHashMap`, ...

    HASHMAP排序功能描述

    HashMap是Java编程语言中常用的集合类之一,它属于哈希表数据结构,提供key-value的存储方式,并且具有快速查询的特性。然而,HashMap本身并不保证元素的顺序,特别是当涉及到遍历或输出HashMap的内容时,顺序可能会...

    简单的key value hashmap

    哈希映射(HashMap)是Java编程语言中一个非常重要的数据结构,它在《简单的key value hashmap》中被提及,通常用于存储键值对(key-value pairs)。HashMap是Java集合框架的一部分,它提供了高效的查找、插入和删除...

    马士兵老师HashMap学习笔记

    《马士兵老师HashMap学习笔记详解》 HashMap是Java编程语言中常用的一种数据结构,它提供了键值对(key-value pair)的存储功能,是基于哈希表实现的。马士兵老师的HashMap学习笔记深入剖析了这一核心组件的工作...

    一个delphi的hashmap源代码

    在IT行业中,哈希表(HashMap)是一种高效的数据结构,它使用哈希函数将键(Key)映射到数组的特定位置,以便快速存取数据。Delphi是一种强大的Object Pascal编程语言,它提供了多种实现哈希表的方式。在这个特定的...

Global site tag (gtag.js) - Google Analytics