JDK1.7中的hashmap。 HashMap继承自AbstractMap,实现了Map、Cloneable、Serializable接口。 每个Entry的属性: final K key; V value; Entry<K,V> next; int hash; hashmap put方法 public V put(K key, V value 在创建新节点之后,将size++; hashmap get方法 key为空,调用getForNullKey。不为空,调用getEntry。通过key计算对应的桶位置,并遍历该位置的链表获取节点。 、 hashmap resize()方法 void resize(int newCapacity){ Entry[] oldTable=table; int oldCapacity=oldTable.length
前言 现在一般都JDK8了,为什么还要说JDK7呢。因为JDK7和JDK8的hashmap实现不一样,JDK7是用数组+链表实现的,而JDK8是红黑树。学习都是个慢慢渐进的过程。 实现 时间复杂度: 读取 插入 删除 数组 O(1) O(n) O(n) 链表 O(n) O(1) O(1) 上面提到JDK7是用数组+链表实现的,为什么这样做呢? HashMap的键值可以为Null吗?原理是什么? HashMap扩容机制是怎么样的,JDK7和JDK8有什么不同? JDK8中的HashMap有哪些改动? JDK8中为什么要使用红黑树? 为什么重写对象的Equal方法时,要重写HashCode方法,跟HashMap有什么关系吗? HashMap是线程安全的吗?遇到ConcurrentModificationException异常吗? 在使用HashMap的过程中我们应该注意些什么问题?
HashMap 遍历 大体上可以分为4类: 1,迭代器 2,ForEach 遍历 3,lambda 表达式遍历 4,StreamsApi 遍历 但是每种类型下有不同的实现方式,所以又可以分为7种: ? 使用迭代器 EntrySet 的方式遍历 public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap 2,使用迭代器的KeySet public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap EntrySet 的方式进行遍历; public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap 7,使用 Streams API 多线程的方式进行遍历。
用这个来维护内部的数据结构,它的长度由容量决定 int size:HashMap的大小 int threshold:HashMap的极限容量,扩容临界点(容量和加载因子的乘积) 加载因子 1 二、HashMap的数据结构 我们知道在Java中最常用的两种结构是数组和模拟指针(引用),几乎所有的数据结构都可以利用这两种来组合实现,HashMap也是如此。 实际上HashMap是一个“链表散列”,如下是它数据结构: ? 从上图我们可以看出HashMap底层实现还是数组,只是数组的每一项都是一条链。 下面为HashMap构造函数的源码: //无参构造器 public HashMap() { //默认初始容量大小为16,默认的加载因子为0.75f this 如果为null,则调用putForNullKey:这就是为什么HashMap可以用null作为键的原因,来看看HashMap是如何处理null键的: private V putForNullKey
HashMap 遍历 大体上可以分为4类: 1,迭代器 2,ForEach 遍历 3,lambda 表达式遍历 4,StreamsApi 遍历 但是每种类型下有不同的实现方式,所以又可以分为7种: 案例 使用迭代器 EntrySet 的方式遍历 public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap 2,使用迭代器的KeySet public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap EntrySet 的方式进行遍历; public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap integerStringEntry.getKey()); System.out.println(integerStringEntry.getValue()); })); } 结果 7,
测试代码 注意 要在 JDK 7 下运行,JDK7以后否则扩容机制和 hash 的计算方法都变了 public static void main(String[] args) { // 测试 java 7 中哪些数字的 hash 结果相等 System.out.println("长度为16时,桶下标为1的key"); for (int i = 0 <Integer, Integer> map = new HashMap<Integer, Integer>(); // 放 12 个元素 map.put(2, null null); map.put(4, null); map.put(5, null); map.put(6, null); map.put(7, } h ^= k.hashCode(); h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7)
本文先从 HashMap 的遍历方法讲起,然后再从性能、原理以及安全性等方面,来分析 HashMap 各种遍历方式的优势与不足,本文主要内容如下图所示: HashMap 遍历 HashMap 遍历从大的方向来说 但每种类型下又有不同的实现方式,因此具体的遍历方式又可以分为以下 7 种: 使用迭代器(Iterator)EntrySet 的方式进行遍历; 使用迭代器(Iterator)KeySet 的方式进行遍历; Map<Integer, String> map = new HashMap(); map.put(1, "Java"); map.put(2, "JDK System.out.println(entry.getKey()); System.out.println(entry.getValue()); }); } } 7. 总结 本文我们讲了 HashMap 4 种遍历方式:迭代器、for、lambda、stream,以及具体的 7 种遍历方法,综合性能和安全性来看, 我们应该尽量使用迭代器(Iterator)来遍历 EntrySet
JDK 7 中,HashMap 由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。 在 JDK 8 中,HashMap 由“数组+链表+红黑树”组成。 因为在JDK 7 中扰动了 4 次,计算 hash 值的性能会稍差一点点。 我们来举个例子,看下图: 当 length =15时,6 和 7 的结果一样,这样表示他们在 table 存储的位置是相同的,也就是产生了碰撞,6、7就会在一个位置形成链表,4和5的结果也是一样,这样就会导致查询速度降低 如果我们进一步分析,还会发现空间浪费非常大,以 length=15 为例,在 1、3、5、7、9、11、13、15 这八处没有存放数据。 13、HashMap为什么线程不安全? JDK 7 时多线程下扩容会造成死循环。 多线程的put可能导致元素的丢失。 put和get并发时,可能导致get为null。 详情参照这篇
比如下标是0-7,那么我们可以余它的数组大小(length),hashCode是随机的,得出的下标也是随机的,这也使得数组下标随机平均。 ? 源码解析 这里从HashMap的源码上去看HashMap这个结构,从源码分析理解。 HashMap里有这些个属性: // 默认初始容量 16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 // 最大容量 V oldValue = e.value; e.value = value; // 这个方法不管他,跟hashMap
HashMap 遍历 大体上可以分为4类: 1,迭代器 2,ForEach 遍历 3,lambda 表达式遍历 4,StreamsApi 遍历 但是每种类型下有不同的实现方式,所以又可以分为7种: 案例 使用迭代器 EntrySet 的方式遍历 public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap 2,使用迭代器的KeySet public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap EntrySet 的方式进行遍历; public void demo1(){ //创建Map 对象 Map<Integer, String> map = new HashMap integerStringEntry.getKey()); System.out.println(integerStringEntry.getValue()); })); } 结果 7,
本文主要介绍Java7中HashMap的底层实现原理,其他的Map集合,读者可以自行翻阅其他资料进行学习。 二、JDK7中HashMap底层原理 2.1 HashMap在JDK7中的结构 HashMap在JDK7或者JDK8中采用的基本存储结构都是数组+链表形式。 本节主要是研究HashMap在JDK7中的底层实现,其基本结构图如下所示: ? 三、总结 本文着重讲解了JDK7中HashMap的具体实现原理,包括put、get、扩容等内部实现机制,相信读者仔细品读以后,对JDK7中的HashMap的实现会有一个清晰地认识,JDK7中的HashMap 后续将继续讲解JDK8中的HashMap实现原理,届时将对比JDK7,帮助读者掌握两者之间的共性和差异!
1、两者父类不同 HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。 2、对外提供的接口不同 Hashtable比HashMap多提供了elments() 和contains() 两个方法。 HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。 4、安全性不同 HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。 虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,这样设计是合理的,因为大部分的使用场景都是单线程。
6.HashMap的table的容量如何确定?loadFactor是什么?该容量如何变化?这种变化会带来什么问题? 7.HashMap中put方法的过程? 8.数组扩容的过程? 13.HashMap&TreeMap&LinkedHashMap使用场景? 14.HashMap和HashTable有什么区别? 15.Java中的另一个线程安全的与HashMap极其类似的类是什么? 7.HashMap中put方法的过程? 关于这个值的设置,在《阿里巴巴Java开发手册》有以下建议: 也就是说,如果我们设置的默认值是7,经过Jdk处理之后,会被设置成8,但是,这个HashMap在元素个数达到 8*0.75 = 6的时候就会进行一次扩容 如果我们通过initialCapacity/ 0.75F + 1.0F计算,7/0.75 + 1 = 10 ,10经过Jdk处理之后,会被设置成16,这就大大的减少了扩容的几率。
这篇文章仅限小编个人的理解,小编不是Java方向的,只是对Java有很高的学习兴趣 如果有什么不对的地方还望大佬指点 HashMap的底层是数组+链表,(很多人应该都知道了) JDK1.7的是数组 8的时候,会变成红黑树 在红黑树的元素小于6的时候会变成链表 元素进行尾插 HaspMap的数组默认大小为16 数组也叫做Hash桶 (貌似听说这个值和阿里巴巴Java开发手册好像有点关系) HashMap 元素的下标是 HashCode(元素) & (数组的长度-1) HashMap的扩容 Resize 扩容的话,这里有一个值叫做loadFactor(阈值),默认值为0.75; 当数组的 元素数量>数组大小 , JDK1.7的是分段数组,有Segment锁(继承于ReentrantLock)加速一小段保证并发 JDK1.8 是和HashMap一样了,数组+链表(或者红黑树) Synchronized( 这一点与乐观锁,SVN的思想是比较类似的) 使用HashTable(基本是废弃的) HashTable就是把HashMap套上了一个Synchronized Collections.synchronizedMap
JAVA系列面试题 特点 1.高频面试 2.力求精深 3.贴近企业 4.迭代升级 1.HashMap的数据结构 基本功的问题,难度指数:1星; 1.7 数组+链表; 1.8 数组+链表+红黑树 2.当两个对象的 在HashMap这种数据结构上,存储的元素,hashCode一样,如果说,内容一致,则不能添加到同一个HashMap对象;否则,内容不一致,在此结构之后,变成链表格式。 3.初始容量是多少? 基本功,难度指数:2星 首先是HashMap数组的存储结构,在hashCode()冲突的时候,长度够用情况下,变成链表格式; 其次:长度》=8,最小容量是64的情况下,会变成红黑树。 难度指数:3星-4星 考察知识点:HashMap基本功。 设置一个超过阈值范围的,初始容量: 初始容量*0.75>1024 这里使用到了Oracle官方的插件 <! 执行测试 } @Benchmark public void noSizeTest(Blackhole blackhole) { Map map = new HashMap
HashMap和Hashtable的区别 1.共同点:都是双列集合,底层都是哈希算法 2.区别: * 1.HashMap是线程不安全的,效率高,JDK1.2版本 * Hashtable是线程安全的 他也在继续参与SE 7和8的语言发展。之前Neal在为Google的在线日历工作,也曾经是C++标准委员会的一员,并曾在Sun微系统公司,MicroTec研究院和德州仪器领导开发C和C++编译器。 虽然Hashtable比HashMap出现的早一些,但是现在Hashtable基本上已经被弃用了。而HashMap已经成为应用最为广泛的一种数据类型了。 因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。 7 遍历方式的内部实现上不同 Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
HashMap 说到 HashMap 想必大家从脑海中直接复现出了一大堆的面试题, HashMap 的数据结构 JDK7 和 JDK8 HashMap哪里不一样 HashMap是否安全 HashMap 的扩容机制 说到这里,我们就来挨着分析一下这个 HashMap 的这写面试题。 JDK7 和 JDK8 HashMap哪里不一样 JDK7我们大家也都知道,如果按照横向是数组,那么他的纵向每个元素上面,都是一个单向的链表,而横向上,每一个实体,就相当于是一个 Entry 的实例。 而这每一个 Entry 中都包含了四个属性, key value hash值 用于单项列表的next 就像下图这个样子 JDK7 所以 JDK7 的 HashMap 的数据结构就是 数组+链表 的形式构成 HashMap是否安全 一说这个,肯定都是非常基础的面试题,都知道 HashMap 是属于那种线程不安全的类,为什么不安全,他不安全到底会提现在哪个地方,难道面试的时候,你就只会说他的内部没有被 synchronize
因此,在键或值的基础上排序HashMap是一个很难的面试问题,如果你不知道如何解决的话。下面让我们看看如何解决这个问题。 ? 1. HashMap存储每对键和值作为一个Entry<K,V>对象。 例如,给出一个HashMap, ? 键的每次插入,都会有值对应到散列映射上,生成一个Entry <K,V>对象。通过使用这个Entry <K,V>对象,我们可以根据值来排序HashMap。 2.创建一个简单的HashMap,并插入一些键和值。 ? 3.从HashMap恢复entry集合,如下所示。 ? 4.从上述mapEntries创建LinkedList。 7. ele1.getValue(). compareTo(ele2.getValue())——比较这两个值,返回0——如果这两个值完全相同的话;返回1——如果第一个值大于第二个值;返回-1——如果第一个值小于第二个值 由于HashMap不保持顺序,因此我们要使用LinkedHashMap。 ? 10.完整的代码如下。 ? ? ?
it = set.iterator(); while (it.hasNext()) { String s = it.next(); System.out.println(s); } HashMap 定义 import java.util.HashMap; HashMap<String, Integer> map = new HashMap<>(); package com.zhongxin.day08 ; import java.util.HashMap; public class HashMapDemo { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<>(); map.put("id", 100); map.put(
---- 7.HashMap 中的红黑树原理 红黑树 ? LinkedHashMap ?