JavaSE进阶(八)——Set
前言
***本篇笔记最好配合HashMap在JDK1.8发生的变化***阅读,读不懂没关系,慢慢来。
Set集合可以简单地理解为树型结构,树型结构是什么?如下:
树型结构就像是一棵树倒过来放置一样从一棵树干开始分叉。每一次分叉都有一个节点。
本篇笔记记录是哪个Set接口:
- HashSet:底层由哈希表实现;
- TreeSet:底层由红黑树实现;
- LinkedHashSet:底层由哈希表+链表实现。
虽然下一篇笔记才会说明,但是大家先有个印象:Set的底层是由Map实现的。Map的相关概念可以另一篇笔记(虽然不一定能看懂):
Set中有一些共同的要点:
- 不可重复;
- 最多只有一个null值;
Set
HashSet
特点:
- 无序(不保证循序);
- 底层使用HashMap;
- 默认大小为16,默认负载因子0.75;
- 满了(与负载因子有关)自动扩充一倍
HashSet的扩容公式:当前长度 * 负载因子 = 触发扩容的长度。
【注】:负载因子越大,则查询速度越慢。负载因子越小,则空间浪费越大。
方法
Set<String> set = new HashSet<String>();
// 添加方法,注意不能添加重复值
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
set.add("f");
set.add("g");
set.add("h");
// 输出当前Set集合的长度
System.out.println(set.size());
// 按照值来删除元素(因为HashSet无序,所以我们没有索引)
System.out.println(set.remove("a"));
// 遍历的时候可以将set转型为数组,然后遍历
Object[] array = set.toArray();
for (Object object: array
) {
System.out.println(object);
}
// 也可以将set转化为list再进行遍历,方式很多大家自行发挥
List<String> list = new ArrayList<>(set);
for (String str: list
) {
System.out.println(str);
}
重复问题
我们在使用自定义类型操作插入HashSet的时候,会发现对象中属性值一致的时候也可以重复插入,这似乎违反了Set中无法放入重复值的规定。
其实我们在判定插入的元素是否相同的时候需要执行两步操作,首先先判定是否是同一个对象(物理空间),然后在判定对象(或元素值)是否相同。
首先说明第一步,对象与对象之间判定是否为同一个对象是根据对象的HashCode来判定的,HashCode就像身份证一样,每一个对象的HashCode都是唯一的,且无法改变。所以我们先根据HashCode来判定是否为同一个对象,如果是同一个对象就没必要判定对象的属性值了。
当我们判定对象不同时,会执行Equals方法,该方法会将对象中的值进行重复判定,如果有重复的值就不会再插入了(不是覆盖)。
TreeSet
特点:
- 无序;
- 可以排序;
- 无法重复。
自定义类型的对象需要实现compare接口,否则TreeSet中的排序功能无法实现
排序
TreeSet有一个排序的功能,在我们插入的时候就会自动排序。但是自定义类型对象排序的时候需要实现一下接口:
Comparable
上述代码中的尖角号是泛型,后续说明,现在只需要记住尖角号中放需要操作的对象类型。
该接口中有一个compareTo方法需要我们重写,该方法是通过对对象中的属性值进行比较排序,可以自行定义比较哪个属性。
if (this.getSage() < student.getSage()){
return 1;
}else if (this.getSage() == student.getSage()){
return 0;
}
return -1;
- 1:升序;
- 0:相等;
- -1:降序。
LinkedSet
该Set类型是有序的,就算扩充也不会出现乱序。
总结
本篇笔记中记录了Set的使用以及一些简单地逻辑,记录的不多且并不是很详细的原因是从使用频率来说,Set的使用并不是很多。下一篇笔记会记录Map,很重要。大家一起加油。