Java集合精选常见面试题( 九 )

,但是需要注意的是TreeMap它还实现了NavigableMap接口和SortedMap 接口 。
实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力 。
实现SortedMap接口让 TreeMap 有了对集合中的元素根据键排序的能力 。默认是按 key 的升序排序 , 不过我们也可以指定排序的比较器 。示例代码如下:
/** * @author shuang.kou * @createTime 2020年06月15日 17:02:00 */public class Person {private Integer age;public Person(Integer age) {this.age = age;}public Integer getAge() {return age;}public static void main(String[] args) {TreeMap<Person,String> treeMap = new TreeMap<>(new Comparator<Person>() {@Overridepublic int compare(Person person1,Person person2) {int num = person1.getAge() - person2.getAge();return Integer.compare(num,0);}});treeMap.put(new Person(3),"person1");treeMap.put(new Person(18), "person2");treeMap.put(new Person(35), "person3");treeMap.put(new Person(16), "person4");treeMap.entrySet().stream().forEach(personStringEntry -> {System.out.println(personStringEntry.getValue());});}}输出:
person1person4person2person3可以看出 , TreeMap 中的元素已经是按照 Person 的 age 字段的升序来排列了 。
上面 , 我们是通过传入匿名内部类的方式实现的,你可以将代码替换成 Lambda 表达式实现的方式:
TreeMap<Person,String> treeMap = new TreeMap<>((person1,person2) -> {int num = person1.getAge() - person2.getAge();return Integer.compare(num, 0);});综上 , 相比于HashMap来说 TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力 。
HashSet 如何检查重复以下内容摘自《Head first java》第二版:
当你把对象加入HashSet时 , HashSet 会先计算对象的hashcode值来判断对象加入的位置 , 同时也会与其他加入的对象的 hashcode 值作比较 , 如果没有相符的 hashcodeHashSet 会假设对象没有重复出现 。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同 。如果两者相同,HashSet 就不会让加入操作成功 。
HashMap 的底层实现JDK1.8 之前JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列 。HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突 。
所谓扰动函数指的就是 HashMap 的 hash 方法 。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞 。
JDK 1.8 HashMap 的 hash 方法源码:
JDK 1.8 的 hash 方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变 。
static final int hash(Object key) {int h;// key.hashCode():返回散列值也就是hashcode// ^ :按位异或// >>>:无符号右移,忽略符号位,空位都以0补齐return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}所谓 “拉链法” 就是:将链表和数组相结合 。也就是说创建一个链表数组,数组中每一格就是一个链表 。若遇到哈希冲突,则将冲突的值加到链表中即可 。

Java集合精选常见面试题

文章插图
DK1.8 之后相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间 。
TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树 。红黑树就是为了解决二叉查找树的缺陷 , 因为二叉查找树在某些情况下会退化成一个线性结构
HashMap 的长度为什么是 2 的幂次方为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀 。我们上面也讲到了过了,Hash 值的范围值-2147483648 到 2147483647,前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的 。但问题是一个 40 亿长度的数组 , 内存是放不下的 。所以这个散列值是不能直接拿来用的 。用之前还要先做对数组的长度取模运算 , 得到的余数才能用来要存放的位置也就是对应的数组下标 。这个数组下标的计算方法是“

推荐阅读