【搞定Java集合类】原理

HashMap 的工作原理是什么?

我们知道在 Java 中最常用的两种结构是数组和模拟指针(引用),几乎所有的数据结构都可以利用这两种来组合实现,HashMap 也是如此。实际上 HashMap 是一个**“链表散列”**。

HashMap 是基于 hashing 的原理。

  • 我们使用 #put(key, value) 方法来存储对象到 HashMap 中,使用 get(key) 方法从 HashMap 中获取对象。
  • 当我们给 #put(key, value) 方法传递键和值时,我们先对键调用 #hashCode() 方法,返回的 hashCode 用于找到 bucket 位置来储存 Entry 对象。

?? 当两个对象的 hashCode 相同会发生什么?

因为 hashcode 相同,所以它们的 bucket 位置相同,“碰撞”会发生。

因为 HashMap 使用链表存储对象,这个 Entry(包含有键值对的 Map.Entry 对象)会存储在链表中。

?? hashCode 和 equals 方法有何重要性?

HashMap 使用 key 对象的 #hashCode() 和 #equals(Object obj) 方法去决定 key-value 对的索引。当我们试着从 HashMap 中获取值的时候,这些方法也会被用到。

  • 如果这两个方法没有被正确地实现,在这种情况下,两个不同 Key 也许会产生相同的 #hashCode() 和 #equals(Object obj) 输出,HashMap 将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。

同样的,所有不允许存储重复数据的集合类都使用 #hashCode() 和 #equals(Object obj) 去查找重复,所以正确实现它们非常重要。#hashCode() 和 #equals(Object obj) 方法的实现,应该遵循以下规则:

  • 如果 o1.equals(o2) ,那么 o1.hashCode() == o2.hashCode() 总是为 true 的。
  • 如果 o1.hashCode() == o2.hashCode() ,并不意味 o1.equals(o2) 会为 true 。

?? HashMap 默认容量是多少?

默认容量都是 16 ,负载因子是 0.75 。就是当 HashMap 填充了 75% 的 busket 是就会扩容,最小的可能性是(16 * 0.75 = 12),一般为原内存的 2 倍。

?? 有哪些顺序的 HashMap 实现类?

  • LinkedHashMap ,是基于元素进入集合的顺序或者被访问的先后顺序排序。
  • TreeMap ,是基于元素的固有顺序 (由 Comparator 或者 Comparable 确定)。

?? 我们能否使用任何类作为 Map 的 key?

我们可以使用任何类作为 Map 的 key ,然而在使用它们之前,需要考虑以下几点:

  • 1、如果类重写了 equals 方法,它也应该重写 hashcode 方法。
  • 2、类的所有实例需要遵循与 equals 和 hashcode 相关的规则。
  • 3、如果一个类没有使用 equals ,你不应该在 hashcode 中使用它。
  • 4、用户自定义 key 类的最佳实践是使之为不可变的,这样,hashcode 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashcode 和 equals 在未来不会改变,这样就会解决与可变相关的问题了。

比如,我有一个 类MyKey ,在 HashMap 中使用它。代码如下:

//传递给MyKey的name参数被用于equals()和hashCode()中MyKey key = new MyKey(‘Pankaj‘); //assume hashCode=1234myHashMap.put(key, ‘Value‘);// 以下的代码会改变key的hashCode()和equals()值key.setName(‘Amit‘); //assume new hashCode=7890//下面会返回null,因为HashMap会尝试查找存储同样索引的key,而key已被改变了,匹配失败,返回nullmyHashMap.get(new MyKey(‘Pankaj‘));
  • 那就是为何 String 和 Integer 被作为 HashMap 的 key 大量使用。

?? HashMap 的长度为什么是 2 的幂次方?

为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。

这个算法应该如何设计呢?我们首先可能会想到采用 % 取余的操作来实现。但是,重点来了:

  • 取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说 hash % length == hash & (length - 1) 的前提是 length 是 2 的 n 次方;)。
  • 并且,采用二进制位操作 &,相对于 % 能够提高运算效率,

这就解释了 HashMap 的长度为什么是 2 的幂次方。

HashSet 的工作原理是什么?

HashSet 是构建在 HashMap 之上的 Set hashing 实现类。让我们直接撸下源码,代码如下:

// HashSet.java

private transient HashMap<E,Object> map;

private static final Object PRESENT = new Object();
  • map 属性,当我们创建一个 HashMap 对象时,其内部也会创建一个 map 对象。后续 HashSet 所有的操作,实际都是基于这个 map 之上的封装。
  • PRESENT 静态属性,所有 map 中 KEY 对应的值,都是它,避免重复创建。

OK ,再来看一眼 add 方法,代码如下:

// HashSet.java

public boolean add(E e) {    return map.put(e, PRESENT) == null;}
  • 是不是一目了然。

?? HashSet 如何检查重复?

艿艿:正如我们上面看到 HashSet 的实现原理,我们自然可以推导出,HashMap 也是如何检查重复滴。

如下摘取自 《Head First Java》 第二版:

当你把对象加入 HashSet 时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较。

如果没有相符的 hashcode ,HashSet会假设对象没有重复出现。

但是如果发现有相同 hashcode 值的对象,这时会调用 equals 方法来检查 hashcode 相等的对象是否真的相同。

  • 如果两者相同,HashSet 就不会让加入操作成功。
  • 如果两者不同,HashSet 就会让加入操作成功。

EnumSet 是什么?

java.util.EnumSet ,是使用枚举类型的集合实现。

当集合创建时,枚举集合中的所有元素必须来自单个指定的枚举类型,可以是显示的或隐示的。EnumSet 是不同步的,不允许值为 null 的元素。

它也提供了一些有用的方法,比如 #copyOf(Collection c)#of(E first, E... rest) 和 #complementOf(EnumSet s) 方法。

关于 EnumSet 的源码解析,见 《EnumSet 源码分析》 文章。

TODO TreeMap 原理

Java 中的 TreeMap 是使用红黑树实现的。

TODO TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?

等到源码解析后,在进行补充。

Java Priority Queue 是什么?

PriorityQueue 是一个基于优先级堆的无界队列,它的元素都以他们的自然顺序有序排列。

在它创建的时候,我们可以可以提供一个比较器 Comparator 来负责PriorityQueue 中元素的排序。

PriorityQueue 不允许 `` null元素,不允许不提供自然排序的对象,也不允许没有任何关联 Comparator 的对象。

最后,PriorityQueue 不是线程安全的,在执行入队和出队操作它需要 O(log(n)) 的时间复杂度。

?? poll 方法和 remove 方法的区别?

poll 和 remove 方法,都是从队列中取出一个元素,差别在于:

  • poll 方法,在获取元素失败的时候会返回空
  • remove() 方法,失败的时候会抛出异常。

?? LinkedHashMap 和 PriorityQueue 的区别是什么?

  • PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,LinkedHashMap 维持的顺序是元素插入的顺序。
  • 当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。

原文地址:https://www.cnblogs.com/zwhu1216/p/11386220.html

时间: 2024-08-28 15:56:29

【搞定Java集合类】原理的相关文章

【搞定Java集合类】区别

List 和 Set 区别? List,Set 都是继承自 Collection 接口. List 特点:元素有放入顺序,元素可重复. Set 特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉. 注意:元素虽然无放入顺序,但是元素在 Set 中的位置是有该元素的 hashcode 决定的,其位置其实是固定的. 另外 List 支持 for 循环,也就是通过下标来遍历,也可以用迭代器,但是 Set 只能用迭代,因为他无序,无法用下标来取得想要的值. Set 和 List 对比: Set:检索指

几周内搞定Java的10个方法

不要将Java与JavaScript弄混了,Java的目标是“一次编译,到处调试”(呃,不对,是“到处运行”).简单来说,就是Java程序可以直接在任何设备上运行. Java语言是什么? 不管我们是否意识到,实际上我们基本每天都在与Java打交道.在浏览网页时,可能会弹出一个提示,要求必须安装Java才能继续浏览.这种情况一般发生在使用flash或者是通过某种方式将flash组件集成到核心系统的站点. Java并不是那种通常在新电脑上直接下载下来就能用的程序.我不能确定有没有操作系统将Java作

如何四个月搞定java?

目前国内从事软件开发的,java的占比是越来越高,不说别的在培训视频的绝对量上,java几乎占据了半壁江山,很多小伙伴直接从网上搜到一些视频,然后制定好计划开始了java的学习计划,然后大部分的开始对着视频学的非常有意思,但是慢慢的学到一半,发现离了视频不知道就不知道怎么写的代码了.然后让东西写代码一些很常见的程序也是别别扭扭的才弄出来,全然没了看视频的那种自信满满的感觉.但是一边看视频一边有老师指导效果就会不一样,有不懂得问题都可以找老师解答.有什么问题也可进java学习qun52159582

选课不再纠结!10个课程搞定Java!

身在茫茫的课程海洋的童鞋们,是不是很迷茫?数百个课程我该学哪个?这个课程质量怎样啊?学院课程小管家整理出了最受欢迎的10个Java类视频课程~迷途中的童鞋们终于不再纠结了!!!10各课程包含了初中高3各阶段,小管家是不是很贴心^_^ 大家有想要的专题欢迎在评论中说明哟,管家会根据需求的情况,进行下一个专题的选择~~~~ (一)  小白进阶必学~ 1.http://edu.51cto.com/course/course_id-4191.html 刘英杰 课程名称:JavaWeb(JSP+servl

一键搞定Java桌面应用安装部署 —— exe4j + Inno Setup 带着JRE, 8M起飞

转载自:http://www.blogjava.net/huliqing/archive/2008/04/18/193907.html 对于作Java桌面应用来说,比较烦人的就是安装部署问题,客户端是否安装有jre.jre版本问题.jre去哪下载.如何用jre启动你的Java应用?不要说刚接触电脑的人,就算是比较熟悉电脑,如果没有接触过Java,面对一个Java应用,如何在Windows下启动它,估计都会折腾半天.如果不是因为这个问题,Java在我的眼里算是最完美的语言了,也是我最喜爱的语言.

几行代码搞定java生成解析二维码功能

最近公司要求扫描二维码和生成二维码的功能.而群里部分网友也提到了.我这里就写了一个demo,和大家分享.代码很简介,希望大家能够喜欢. 网友表示在网上搜索了很多,发现不是代码不全,就是jar不匹配. 我这里共享了一个zxing的2.2版本的例子,也提供了下载地址.实现代码如下: package com.herman.test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcep

一文搞定vue-router实现原理

vue-router是什么? 首先我们需要知道vue-router是什么,它是干什么的? 这里指的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器.替换,vue-router就是WebApp的链接路径管理系统. vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页应用. 那与传统的页面加快有什么区别呢? 1.vue的单页面应用是基于路由和组件的,路由用于设置访问路径,转换路径和组件映射起来. 2.传统的页面应用,是用一

从原理上搞定编码(一)-- 初识编码

编码问题一直都伴随着程序猿从不间断,刚开始学编程的时候好多次遇到编码问题,解决了文件读取的编码问题,又遇到了网络编码问题,解决了网络编码问题又遇到了数据库编码问题.总结一下无非就是编码原理没搞清楚,希望本文能从原理上让菜鸟们理解编码,遇到问题可以从原理上搞定编码. 一.编码 人类先有了自己的语言,交流了若干个世纪,然后出现了计算机.可惜计算机只认0和1,人类只能认文字,双方都不能妥协,那就必须要有一个从文字到0.1的映射了.从文字到0.1的映射称为编码,反过来从0.1到文字叫解码. 具体什么是编

[转] Java程序员学C#基本语法两个小时搞定(对比学习)

Java程序员学C#基本语法两个小时搞定(对比学习) 对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想. 1.引包 using System;java用import2.构造函数和java语法相同3.析构函数  变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销.  类的对象被撤销时,将自动调用析构函数.一些善后工作可放在析构函数中完成.  析构函数的名字为~类名,无返回类型,也无参数.Per