- 浏览: 304479 次
- 性别:
- 来自: 山西
博客专栏
-
天天编程
浏览量:21977
最新评论
-
变脸小伙:
运用到了场景中,希望接力
StringBuffer源码理解 -
fangsj:
IE9 安全设置 把这个禁用掉了
spring mvc 文件上传+本地预览+一次提交 -
xu-ch:
今天面试,遇到这题,求出了相似度,面试官问我算法原理是什么,悲 ...
计算字符串相似度算法——Levenshtein -
flywangfei:
你是创新工场的么?
计算字符串相似度算法——Levenshtein -
scwuwei:
六点起床比较好
《4点起床-最养生和高效的时间管理》读书笔记
看看HashMap对应的源码。
1.类、接口关系
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
克隆和序列化不懂,先看Map。
2.实现的接口 Map
public interface Map<K,V> { //这些方法就不用写注释了吧,一看就懂。 int size(); boolean isEmpty(); boolean containsKey(Object key); boolean containsValue(Object value); V get(Object key); V put(K key, V value); V remove(Object key); void putAll(Map<? extends K, ? extends V> m); void clear(); //这里是返回的不重复的集合 Set<K> keySet(); //这里返回的是一个集合 Collection<V> values(); Set<Map.Entry<K, V>> entrySet(); boolean equals(Object o); int hashCode(); //还有一个内部的接口,这个相当于一个key-value数据。 interface Entry<K,V> { K getKey(); V getValue(); V setValue(V value); boolean equals(Object o); int hashCode(); } }
3.继承的抽象类AbstractMap
这个方法有700行,我就挑出几个说一说
3.1构造方法
public abstract class AbstractMap<K,V> implements Map<K,V> { protected AbstractMap() { } }
3.2 几个方法
简单介绍几个,都没什么实际意思。
//声明了抽象变量,里面直接是Map的内部类,这个Set包含着所有的数据key+value public abstract Set<Entry<K,V>> entrySet(); //Map长度 public int size() { return entrySet().size(); } //是否为空 public boolean isEmpty() { return size() == 0; } //是否包含某个值 public boolean containsValue(Object value) { //要查找出是否包含,于是开始遍历这个Set。 //这里可以使用 for each,但是这个方法写于jdk1.2,所以当时还没有。遍历的过程很简单。 Iterator<Entry<K,V>> i = entrySet().iterator(); //这里还有一个判空。 if (value==null) { while (i.hasNext()) { Entry<K,V> e = i.next(); if (e.getValue()==null) return true; } } else { while (i.hasNext()) { Entry<K,V> e = i.next(); //注意:比较用的是equals,而不是== if (value.equals(e.getValue())) return true; } } return false; } //查找这个值,一样遍历Set public V get(Object key) { Iterator<Entry<K,V>> i = entrySet().iterator(); if (key==null) { while (i.hasNext()) { Entry<K,V> e = i.next(); if (e.getKey()==null) return e.getValue(); } } else { while (i.hasNext()) { Entry<K,V> e = i.next(); if (key.equals(e.getKey())) return e.getValue(); } } return null; } //这个方法是要求重写的。 public V put(K key, V value) { throw new UnsupportedOperationException(); } //删除某个键值对 public V remove(Object key) { Iterator<Entry<K,V>> i = entrySet().iterator(); Entry<K,V> correctEntry = null; //key值可以为null,查出相应key值所对应的键值对。 if (key==null) { while (correctEntry==null && i.hasNext()) { Entry<K,V> e = i.next(); if (e.getKey()==null) correctEntry = e; } } else { while (correctEntry==null && i.hasNext()) { Entry<K,V> e = i.next(); if (key.equals(e.getKey())) correctEntry = e; } } //这里开始删除操作 V oldValue = null; if (correctEntry !=null) { oldValue = correctEntry.getValue(); //用迭代器的删除方法。 i.remove(); } //返回删除的value。 return oldValue; } //添加一个Map操作,直接循环添加 public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } //直接使用了set的clear方法 public void clear() { entrySet().clear(); }
AbstractMap就看到这里,毕竟主要是看HashMap。
4.HashMap
4.0文字介绍
HashMap首先会开辟一个16空间的Entry(键值对)数组,
Entry类中包括
final K key; V value; Entry<K,V> next; final int hash;
然后对存入的key进行hash运算,算到一个数字(1-15),存入对应位置。
如果对应位置一有数据,则替代当前数据,然后将原数据的引用给新数据,即赋值给next。当存到一定的值后,会扩展空间。
查询的时候将key进行计算得到(1-15),然后查询对应位置,一直去next,知道取到数据或结束。
4.1构造函数。
public HashMap() { //一个倍数 0.75f this.loadFactor = DEFAULT_LOAD_FACTOR; //临界值 16*0.75=12,表示存了12个值以后要开辟新的空间 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); //初始空间16 table = new Entry[DEFAULT_INITIAL_CAPACITY]; //init里面没有内容。 init(); }
4.2 put方法
public V put(K key, V value) { //如果key为空,单独做插入操作。 if (key == null) //插入key为null的值,返回之前此处的值。 return putForNullKey(value); //计算hash值,下面的两个方法 int hash = hash(key.hashCode()); //根据hash值获得在数组中的位置 int i = indexFor(hash, table.length); //循环位置。直到为空位置 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; //如果包含key值和hash值都相等。 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //将原有的键值对的value值替换。 V oldValue = e.value; e.value = value; //这是替换要执行的方法,现在内部还没有处理。 e.recordAccess(this); //将就得value返回。 return oldValue; } } //这里代表这个key值要新加入这个map。 //修改次数 modCount++; //加入 addEntry(hash, key, value, i); return null; } //添加一个空的key。添加到数组的0位置。 private V putForNullKey(V value) { //判断对应位置时候为空, for (Entry<K,V> e = table[0]; e != null; e = e.next) { //对应key值不为空 if (e.key == null) { //更新数据。 V oldValue = e.value; e.value = value; e.recordAccess(this); //返回被替换掉的值。 return oldValue; } } modCount++; //添加一个0位置的。 addEntry(0, null, value, 0); return null; } //添加一个键值对,传入对应hash值和数组位置。 void addEntry(int hash, K key, V value, int bucketIndex) { //将这个位置的原有值取出来, Entry<K,V> e = table[bucketIndex]; //新建一个,传入hash值,还有它的下一个键值对。所以同一个位置,后面插入的数据会查询快一点点。 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); //如果size大于临界值。 if (size++ >= threshold) //将原来空间扩大为两倍 resize(2 * table.length); } //开辟新空间 void resize(int newCapacity) { Entry[] oldTable = table; //旧数组的长度 int oldCapacity = oldTable.length; //最大空间数。 if (oldCapacity == MAXIMUM_CAPACITY) { //边界值为int的最大值。 threshold = Integer.MAX_VALUE; //结束。 return; } //新建一个数组,大小为传入的数字。 Entry[] newTable = new Entry[newCapacity]; //转移新数组 transfer(newTable); //给table赋值。 table = newTable; //边界值更新。 threshold = (int)(newCapacity * loadFactor); } //将数据全部重新插入新数组,规则使用新得数组空间值计算。 void transfer(Entry[] newTable) { //这里用的是旧数组。 Entry[] src = table; int newCapacity = newTable.length; //遍历旧的数组, for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; //为空的键值对不用另外做处理 if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; //根据hash值获取对应数组位置。 int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } }
4.3 get方法
//获取key值对应信息。 public V get(Object key) { //key为null时候 if (key == null) //直接从0号位置开始查。 return getForNullKey(); //计算key值对应hash。 int hash = hash(key.hashCode()); //使用indexFor计算出对应table的位置,然后开始遍历。直到找到或者结束。 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; } //获取key值为null的value。 private V getForNullKey() { //先查找0号位,然后依次找next,直到找到key为null的值或键值对为空,返回。 for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; }
4.4一些常用的方法
4.4.1 public boolean containsKey(Object key)
将key进行hash计算,然后找到键值对,然后返回结果
4.4.2 public boolean containsValue(Object value)
遍历数组,知道找到value。很费性能。
4.4.3 public void clear()
遍历数组,全部置为null。
4.4.4 public V remove(Object key)
找出对应key,去掉,更新关联值。
4.4.5 public Set<K> keySet()
将整个数组返回,这个Set是单独实现的一个类,里面重新实现了迭代器 ,size,包含,清除,将迭代的结果返回Map的key。其实就是返回了完整的Map内容。没有开辟任何多余空间,所以这个方法会很快。
4.4.6 public Collection<V> values()
这个和上面的一样,直接返回的是Map的整体数据,将Collection实现了迭代器 ,size,包含,清除,将迭代的结果返回Map的value。
5.使用的hash算法
int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } static int indexFor(int h, int length) { return h & (length-1); }
计算都是通过hashCode来计算,所以存入HashMap的对象最好不要将hashCode写成一个值。hashCode各个类都有自己的生成方法,似乎Object默认的是内存位置,但是刚刚观察Integer和String都覆写了的。
int占32位,无符号左移20位,空位补0,无符号右移20位。算了,这个二进制运算符要系统的看,先这样把。
6.结束
看到这里都差不多了,主要是理解HashMap的结构和hash算法。
同时产生了两个疑问,先记下来。
疑问一:只实现抽象类AbstractMap不行么?为什么还要implements Map ,不重复吗?
疑问二:transient volatile 这样声明变量是为什么?
发表评论
-
fastcgi中的多线程使用
2012-04-06 22:38 137930.背景 在项目中加入了 ... -
crc循环校验原理和实现
2012-03-29 23:33 194231.CRC简介 CRC(cyclical redundanc ... -
TreeMap源码理解
2012-01-31 10:44 01.首先看构造方法 public TreeMap() { ... -
StringUtils源码理解(下)
2012-01-16 15:46 2260本文介绍StringUtils的剩下的两个方法 1. ... -
StringUtils源码理解(中)有点意思的方法
2012-01-12 00:17 3700这次不按照前面的介绍了,方法都大同小异,下面就介绍几个有意思一 ... -
StringUtils源码理解(上)
2012-01-11 23:08 4812StringUtils 源码,使用的是commons-lang ... -
Properties源码理解
2012-07-05 12:23 3961Properties用来读配置文件 ... -
字符流(一)Reader和Writer源码理解
2011-11-27 20:32 15191.Reader 1.1 继承关系 public ... -
字符流(二)BufferedReader和BufferedWriter源码理解
2011-11-27 20:33 48541.BufferedReader 1.1 继承关系 ... -
DataInputStream和DataOutputStream源码理解
2011-11-17 00:02 44661.FilterInputStream简介 列出主要的内 ... -
InputStream,OutputStream源码理解
2011-11-09 22:50 34201.理解字节流和字符流 按流的形式分: 字节流和字符流。 ... -
File源码理解
2011-11-07 23:55 44041.构造函数 最基本的构 ... -
Thread源码理解
2011-10-23 14:36 43611.首先看一下Runnable接口 ... -
泛型简单回顾
2011-09-06 23:36 1342泛型的简介 1.java引入泛型的好处是安全简单。 2. ... -
LinkedList源码理解
2011-08-31 00:26 1455LinkedList源码 0.首先这个类中的两个变量 ... -
Vector源码理解
2011-08-29 23:44 1548Vector类 1.系统的变量 //记录元素的数组 pr ... -
ArrayList源码理解
2011-08-15 21:02 1764构造方法: ... -
Arrays源码理解
2011-08-15 20:34 13781.equals public static boo ... -
StringBuffer源码理解
2011-06-22 19:39 5725StringBuffer 存储和操作字符串 它所继承实现的类 ...
相关推荐
HashMap 是 Java 中最常用的集合类之一,它是基于哈希表实现的,提供了高效的数据存取功能。HashMap 的核心原理是将键(key)通过哈希函数转化为数组...理解HashMap的内部机制对于优化代码性能和避免潜在问题非常重要。
HashMap是Java编程语言中最常用的集合类之一,它提供了一种基于...通过深入理解这些细节,开发者可以更好地利用HashMap,避免潜在的问题,并优化性能。对于学习者来说,阅读源码并结合实践是掌握HashMap的最好方式。
《HashMap源码剖析》 HashMap是Java编程语言中一个非常重要的数据结构,它属于集合框架的一部分,提供了键值对(Key-Value)的存储方式。HashMap在内部使用了一个数组和链表来实现,实现了快速的查找、插入和删除...
《手写HashMap源码解析——深入理解数据结构与算法》 HashMap是Java编程语言中一个常用的集合类,它提供了一种高效、灵活的键值对存储方式。在面试过程中,尤其是2020年及以后的技术面试中,深入理解HashMap的实现...
在Java编程语言中,HashMap是集合框架中一个重要的类,用于存储键值对的数据结构。面试中,HashMap的源码分析与实现是一个常见的考察点,...深入学习和实践HashMap源码,能够帮助我们更好地理解和优化Java应用程序。
《HashMap 源码解析——JDK11版本》 HashMap是Java中广泛使用的非同步散列表,其设计和实现是高效且灵活的。在JDK1.8之前,HashMap的底层数据...理解HashMap的源码对于深入学习Java集合框架和数据结构具有重要意义。
本篇技术文档将深入剖析这两类数据结构的源码,帮助开发者理解其内部实现原理,提升在实际开发中的应用能力。 HashSet类是基于HashMap实现的,它不包含重复元素,并且不保证集合中元素的顺序。在HashSet中,元素的...
通过分析和学习易语言HashMap类的源码,开发者可以深入理解哈希表的工作原理,以及易语言如何实现高效的数据结构。这对于提升编程技能,尤其是理解和优化数据结构的性能,具有很大的帮助。同时,源码也可以作为参考...
HashMap是Java中常用的一种数据结构,它用于存储键值对,提供快速的存取操作。...因此,理解HashMap的源码有助于我们更好地利用它,并在需要时选择更适合的数据结构,如ArrayList、LinkedList或TreeMap。
本人源码阅读理解, 基于jdk1.7 和jdk 1.8的java HashMap源码的理解, 希望对你理解java源码有作用
HashMap源码中的位运算符&详解 HashMap源码中使用了大量的位运算符&,这些运算符在HashMap的实现中扮演着重要的角色。本文将详细介绍HashMap源码...这些知识点对于学习HashMap源码和理解位运算符&的应用场景非常重要。
HashMap的源码解析涉及到的数据结构主要包括数组和链表,以及在负载过大时升级为红黑树的优化策略。理解这些机制对于高效使用HashMap和解决相关问题至关重要。此外,HashMap是非线程安全的,如果在多线程环境下使用...
Java集合系列之HashMap源码分析 Java集合系列之HashMap源码分析是Java集合系列中的一篇非常重要的...HashMap源码分析是非常重要的,它能够帮助我们更好地理解HashMap的内部机制和实现原理,从而更好地使用HashMap。
HashMap 中红黑树 TreeNode 的 split 方法源码解读 HashMap 中红黑树 TreeNode 的 split 方法是...通过对 split 方法的源码解析,我们可以更好地理解红黑树的实现机制和关键算法,从而更好地应用 Java 中的 HashMap。
总结起来,深入理解Java 7 HashMap的源码,可以帮助我们更好地利用这个工具,同时也能为设计自己的数据结构提供灵感。在JUC(Java并发编程)领域,理解这些底层机制对于编写高效并发代码至关重要。对于系统开源项目...
深入理解Java中的HashMap源码,有助于我们更好地掌握这个常用数据结构的工作原理。HashMap是Java集合框架的重要组成部分,它实现了Map接口,提供了一种快速查找、插入和删除元素的方式。本文将详细解析HashMap的内部...