Java集合框架中treeSet和hashSet的坑!
我以两道题目的形式来引入问题,分析问题,解决问题。
题目一:分析下列程序片段是否会报错,如果报错,报什么类型的错?
public class test { public static void main(String[] args) { TreeSet treeSet = new TreeSet(); treeSet.add(new huangs()); } } class huangs{ }
分析&解答:TreeSet实现了Set接口,是集合框架中为数不多的能对集合中元素进行排序的集合类。那么为什么它可以这种排序功能了,分析其源码才知道,它将元素转化为Comparable类型,调用的其compare方法实现的这种排序。
所以显然题目一的答案已经水落石出———会报错!那么报什么类型的错了,显然经过前面的分析,他会报类的转换错误,即ClassCastException。
原因是huangs类没有实现Comparable接口,也没有在treeSet的构造器中传入指定的比较器。
报错信息如下图所示

TreeSet源码如下图所示

题目二:下面这段代码的输出是什么?
public class HomeWork4 { public static void main(String[] args) { HashSet hashSet = new HashSet(); Person p1 = new Person(1001,"AA"); Person p2 = new Person(1002,"BB"); hashSet.add(p1); hashSet.add(p2); p1.name="CC"; hashSet.remove(p1); System.out.println(hashSet); hashSet.add(new Person(1001,"CC")); System.out.println(hashSet); hashSet.add(new Person(1001,"AA")); System.out.println(hashSet); } } //Person类重写了equals和hashcode方法 class Person{ int id; String name; public Person(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return id == person.id && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(id, name); } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
这题难度很大,没有仔细看过源码的,或者对底层结构不清楚的一般都会弄错!!!
分析&解答:hashSet底层是hashMap,存储的是key-value的双列键值对,只不过hashSet的value存储的是一个固定值。底层的结构是table数组+结点类型为node的链表+红黑树。
添加元素是先通过key值计算其hash值,决定其在table数组上索引,若该索引位置上无元素,则直接存放;若有元素,即产生了hash碰撞,依次比较链表中元素(equals方法),相同则加入失败(要区别于hashmap,它是相同就进行value值的替换),不相同就插入到链表表尾,然后判断是否需要树化(树化条件:链表长度>=8,table数组长度>=64)。
此题中先加入p1,p2,因为id和name都不同所以不会散列到同一位置,下一步将p1的name改为“CC”,此时p1本应该不属于现在的位置,因为name的变化会导致hash值变化,从而导致索引的变化。因此删除p1会根据id和新的name重新计算索引,而新的索引并没有元素,所有删除失败。之后又加入了一个新的person,其值和p1一样,所有其会散列到别的不同于p1的位置,所以新对象加入成功。当加入最后一个对象时,因为其属性和老的p1一样,所以hashcode一样,会被散列到和p1相同的位置,但是由于name不一样,equals方法返回为false,即不是同一个对象,会被添加进链表,并且是链接在p1后面。
运行结果如下,你做对了嘛!

底层结构如下所示:


浙公网安备 33010602011771号