Java基础(十三)——Collections、TreeSet、HashSet
一、Collections
1、Collections——针对Collection的工具类
一次性添加多个对象:
排序:
二、Set——无序不可重复集合
1、TreeSet
TreeSet的底层是红黑树。到红黑树之前会先经历二叉树。 底层代码块是 TreeMap
a、二叉树的增删改查
TreeSet<Integer> set = new TreeSet<>();
System.out.println("---------添加--------");
set.add(3);
set.add(7);
set.add(5);
set.add(9);
set.add(2);
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
Integer num = it.next();
System.out.println(num);
}
System.out.println("-------删除---------");
set.remove(3);
System.out.println(set);
System.out.println("-------修改---------");
set.remove(2);
set.add(5);
2、TreeSet添加功能的底层代码
在红黑树代码执行之前,会把数据先形成一个二叉树结构,再用红黑树进行平衡。这里先学习二叉树底层实现:
下面注释中: 红色文字为第一次执行结果。 蓝色文字为第二次执行结果。 绿色文字为第三次执行结果。 深灰色文字为第四次执行结果。 画xx的那部分,后面再讲。
该图观看顺序:从左到右,从上到下。每次所用到的代码区都可能不同。
a、底层代码详解——第一次添加
1、首先是TreeSet 的无参构造方法,创建TreeMap对象,赋值给下面的有参构造方法,赋值给m,然后又赋值给
下面的m。
2、然后到 add()方法,return 那块的m.put,m就是TreeMap。接着就是调用 put 方法。
3、put方法这里,key 就是传递进来的值“5”,就是add方法里面返回值的put方法的形参 e,旁边的形参看全
大写和斜体就知道是静态常量,这个值哪里都一样,不改变,就不用管。
4、接着到Entry,以及旁边的root,点击打开可以发现,这个root没有值,之前的代码也没有赋值,所以
t == null。
5、接着执行到if(t == null)里面的代码。然后到compare方法,传递了两个5进去,接着执行方法,可以发现
这个方法没有接收返回值,只要不报错就不影响后续代码执行。点击comparator,进去里面可以看到是null,然
后执行三目运算,执行冒号左边,可以发现这里在做强转。点击Comparable发现里面是个接口,所以要求k1要实现
Comparable接口,没实现不能强转。这里k1是“5”,是要存储进集合的对象。这里的代码就要求做一件事,要求
存储的对象实现Comparable接口,且不能为null。不实现会报类型转换异常,为null会空指针异常。
6、接着到root=new Entry那行,这里key是5,储存进去,(这里代码在图片右下角)存储进去以后,顺明这里
开始有第一个节点了,此时开始,root代表第一个节点。
7、然后执行到 return null,第一次添加就结束了。
b、底层代码详解——第二次添加
敬请期待...
3、重写方法,自定义实现排序功能
敬请期待...
自然排序:存储进 TreeSet 集合的对象类必须实现 Comparable 接口,重写 conpareTo(),在 compareTo 里制定排序规则,返回正数存储的对象往右子树方向,返回负数存储的对象往左子树方向,返回0则不存储。
4、比较器
敬请期待...
比较器:在创建 TreeSet 集合对象的时候,传递 Comparator 接口的实现类对象,重写 Comparator 接口的 compare(),在 compare()里指定排序规则。
5、自然排序 对比 比较器
自然排序要重写 Comparable 接口的conpareTo()方法。 比较器要重写 Comparator 接口的 compare()方法。
- 使用自然排序需要改变存储对象类的结构。
- 使用比较器会额外添加多一个类(匿名内部类)。
- 当比较器与自然排序共存的时候,使用比较器进行排序。
6、TreeSet总结
1、无序可排序,不可重复,不能存储 null 对象的集合。 2、使用 TreeSet 存储对象,对象类要么实现自然排序,要么传递比较器
三、HashSet
1、HashSet总结
直接上总结:
HashSet:无序不可重复,可以存储 null 对象。
jdk1.7和jdk1.8不同,有区别:
jdk1.7:hash表(数组+链表) jdk1.8:hash表(动态数组+链表+红黑树)
HashSet存储原理:
通过 HashSet 存储对象,会根据对象的 hashCode() 生成 hash 值,根据 hash 生成索引,根据索引找
到hash 表中的位置;判断位置上是否有对象,没有对象直接插入,有对象则通过 equals() 判断内容是否相同,
如果相同则不插入,不同则根据链表往下检索,直到重复或者链表没有下一个对象,准备插入最后的时候判断链表
是否超过 8 个对象,当链表超过 8 个对象时,总存储的对象大于等于 64 则把链表转换成红黑树当 hash 表
总存储的对象大于负载因子值(0.75),则会进行数组的扩容,扩容为原本的 2 倍。
2、用法
HashSet 的用法跟前面 TreeSet 的用法基本一致,但是要注意,存储自定义对象,如果要实现无序不可重复这个特性,需要实现两个方法:equals()和 hashCode(),只有重写了这两个办法才能实现这个特性,在 IDEA里直接快捷键即可快速出代码: 创建普通的实体类,然后正常使用即可: 可以发现,重复的并没有出现在里面。
|