Collection集合框架当中的Set接口
前言
Set集合继承Colletion 可使用Colletion的常用方法
Collection集合的常用方法
boolean add(E e) 向集合中添加元素
boolean remove(E e) 将元素从集合中删除
void clear() 清空集合所有的元素
boolean isEmpty() 判断集合是否为空
int size() 获取集合的长度
boolean contains(E e) 判断集合中是否包含指定的元素
Set接口的具体实现:
TreeSet集合、HashSet集合(无序集合没有重复元素)、LinkedHashSet集合)
特点:
1、不允许元素重复
2、没有索引,没有带索引的方法
一、HashSet集合
数据结构:哈希表
1、无序集合,存取顺序可能不一致
2、无重复元素
3、没有索引
4、底层是一个哈希表结构,查询的速度非常的快
HashSet是怎么保证不存入重复元素的
1.根据对象的哈希值计算存储位置,如果当前位置没有元素则直接存入
如果当前位置有元素存在,则进入第二步。
2.当前元素的元素和已经存在的元素比较哈希值,如果哈希值不同则
将当前元素进行存储,如果哈希值相同,则进入第三步。
3.通过equals()方法比较两个元素的内容,如果内容不相同,则将当前
元素进行存储,如果内容相同,则不存储当前元素。
public class Demo1HashSet {
public static void main(String[] args) {
HashSet<String> set=new HashSet<>();
String str="abc";
String str2="abc";
set.add(str);
//得到str的哈希值,发现无重复直接添加进集合
set.add(str2);
//add首先进行哈希值的比较,发现哈希值相同就会掉头equals方法ste.equals(str2)返回trun
//即不存储str2
set.add("通话");
set.add("重地");
System.out.println("通话".hashCode()+"====="+"重地".hashCode());
//add调用hashCode发现两个元素的哈希值相等,然后进行equals内容比较
//返回false,即任然存储“重地”
System.out.println(set);
}
}
DataResult:
1179395=====1179395
[通话, 重地, abc]
数据结构:
JDK8之前: 哈希表(数组 + 链表)
JDK8之后: 哈希表(数组 + 链表 + 红黑树)
哈希表的特点:速度快
数据结构:把元素进行分组(相同哈希值的元素在一组),然后使用链表或者红黑树有将相同哈希值的元素连接到一起。
注意:同一数组索引位置的链表元素超过8位就会转换成红黑树
如果了解HashMap的小伙伴可能会疑问,怎么底层数据结构跟HashMap一样,如果你打开了源码你会发现,它其实是HashMap集合来存储的。
底层无参构造初始化
public HashSet() {
map = new HashMap<>();
}
HashSet存储自定义类型(例如:Person)的元素时必要重写hashCode和equals方法
应用实列:
public class Demo2HashSetStudentType {
private String name;
private int age;
public Demo2HashSetStudentType(){
}
public Demo2HashSetStudentType(String name,int age) {
this.name = name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Demo2HashSetStudentType{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Demo2HashSetStudentType that = (Demo2HashSetStudentType) o;
return age == that.age &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public static void main(String[] args) {
Demo2HashSetStudentType one=new Demo2HashSetStudentType("Arvin",21);
Demo2HashSetStudentType two=new Demo2HashSetStudentType("Arvin",21);
Demo2HashSetStudentType three=new Demo2HashSetStudentType("Arvin",20);
HashSet<Demo2HashSetStudentType> set=new HashSet<>();
set.add(one);
set.add(two);
set.add(three);
//输出set集合发现重复
System.out.println(set);
//查看hashCode和equals结果
System.out.println(one.hashCode());
System.out.println(two.hashCode());
System.out.println(one==two);
System.out.println(one.equals(two));
//发现哈希值不同但是equals方法比较的是地址,返回值是false,所以要重写equals方法
}
}
DataResult:
[Demo2HashSetStudentType{name='Arvin', age=20}, Demo2HashSetStudentType{name='Arvin', age=21}]
1969796844
1969796844
false
true
二、哈希值
十进制的整数,由系统随机给出的(就是对象的地址,是一个逻辑地址,一个模拟出来的地址)
不是实际存储的物理地址
在Object类有一个方法,可以获取对象的哈希值
int hashCode();返回对象的哈希值
方法源码:
public native int hashCode();native代表该方法调用本地操作系统的方法 特殊的哈希值:
1、String类哈希值:重写了hashCode方法
java.lang.String 类 已经重写了父类java.lang.Object的hashCode()方法
所以对于"str".hashCode() 的返回值无论运行多少次都一样。
public class Demo4HashCode {
public static void main(String[] args) {
//对于重写hashcode方法的特殊String
String str=new String("Arvin");
String str2=new String("Arvin");
System.out.println(str.hashCode());
System.out.println(str2.hashCode());
System.out.println("吃饭".hashCode());
System.out.println("睡觉".hashCode());
}
}
DataResult:
63541802
63541802
705994
982664
三、LinkedHashSet
LinkedHashSet extends HashSet
特点:底层是一个哈希表(数组+链表/红黑树)+链表;多了一条链表(存储元素的顺序)保证元素的有序性。
public class Demo3LinkedHashSet {
public static void main(String[] args) {
//HashSet集合底层是一个哈希表,无序不可重复
HashSet<Integer> hash=new HashSet<>();
hash.add(2);
hash.add(1);
hash.add(3);
hash.add(3);
System.out.println(hash);
//LinkedHashSet底层是一个哈希表和一个链表,双链表,其中一个存储元素顺序
//不可重复,但是有序
LinkedHashSet<Integer> linked=new LinkedHashSet<>();
linked.add(2);
linked.add(1);
linked.add(2);
linked.add(3);
System.out.println(linked);
}
}
DataResult:
[1, 2, 3]
[2, 1, 3]
四、TreeSet
TreeSet集合的特点
1.元素有序。元素可以按照一定规则进行排序。具体要取决于构造方法
new TreeSet():根据元素的自然顺序进行排序
new TreeSet(Comparator c):根据指定的比较器进行排序
2.TreeSet集合没有索引。只能通过迭代器、增强for循环进行遍历
3.TreeSet集合不能存储重复元素
无参构造底层:
public TreeSet() {
this(new TreeMap<E,Object>());
}
实列
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
for (Integer integer : set) {
System.out.println(integer);
}
}
结果:
1
2
3
4
有参构造传入 指定的比较器之后:
降序排序
TreeSet<Integer> set = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; //降序排序
}
});
set.add(1);
set.add(2);
set.add(3);
set.add(4);
for (Integer integer : set) {
System.out.println(integer);
}
}
4 3 2 1