点击查看:java集合(List、Set、Map)博客目录
第七节 : Set接口
java.util.Set接口和java.util.List接口一样。同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行扩充,只是比Collection接口更加严格了。与List接口不同的是:Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set集合有多个子类,这里我们介绍 java.util.HashSet、 java.util.LinkedHashSet 这两个集合。
- tips: Set集合取出元素的方式可以采用:迭代器、增强for
7.1 、 HashSet集合介绍
- java.util.HashSet是Set接口的一个实现类,它所存储的元素是不可重复,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet底层的实现其实是一个java.util.HashMap支持。
- HashSet 是根据对象的HashSet值来确定元素在集合中的存储位置,因此具有良好的存储和查找性能。保证元素唯一性的方式依赖于:HashCode与equals方法。
- 不允许存储重复元素,无索引
- 是一个无序集合
- 底层是一个哈希表结构(查询速度非常快)
/** Set 接口的特点: 无重复元素、无索引
*/
import java.util.*;
public class Main{
public static void main(String args[]){
Set<Integer> set = new HashSet<>();
// 使用add方法 往集合中添加元素
set.add("1");
set.add("3");
set.add("2");
set.add("1");
//使用迭代器遍历集合
Iterator it = set.iterator();
while(it.hasNext()){
Integer n = it.next();
System.out.println(n); // 输出: 1 2 3
}
//for-each遍历
for(Integer i:set){
System.out.println(i); // 输出: 1 2 3
}
}
}
7.2 、 哈希值
在jdk 1.8之前,哈希表底层采用数组+链表处理冲突,同一hash值的元素都存储在一个链表里,但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而jdk1.8中,哈希表采用 数组+链表+红黑树实现,当链表的长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
import java.util.*;
class Person extends Object{
/**
若重写hashCode方法,则按重写后的哈希函数计算。
*/
}
/**哈希值:是一个十进制整数,有系统随机给出(就是对象的地址值,
* 是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)
* 在Object类中有一个方法可以获取哈希码值
* public native int hashCode();
* native :代表该方法调用的是本地操作系统的方法。
*/
public class Main{
public static void main(String [] args){
//person类继承了Object类,所以可以使用Object类的hashCode方法
Person p1 = new Person();
int h1 = p1.hashCode();
Person p2 = new Person();
int h1 = p2.hashCode();
System.out.println(h1); // 输出: 475266352
System.out.println(h2); // 输出: 1355531311
// String 类的hash值,String 重写了hashCode方法
String s1 = new String("abc");
String s1 = new String("abc");
System.out.println(s1.hashCode()); // 输出: 96354
System.out.println(s2.hashCode()); // 输出: 96354
// hash冲突
System.out.println("重地".hashCode()); // 输出:1179395
System.out.println("通话 ".hashCode()); // 输出:1179395
}
}
7.3 、 HashSet集合存储数据的结构(哈希表)
7.4 、 Set集合存储元素不重复的原理
// 创建HashSet集合对象
HashSet<String> set = new HashSet<>();
String s1 = "abc";
String s2 = "abc";
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
System.out.println(set); // 输出:[重地,通话,abc]
7.5 、 HashSet存储自定义类型元素
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。
创建自定义Student类:
/*
HashSet 存储自定义类型元素
Set集合元素报错元素唯一:
存储的元素(String、......Student 、 person ...),必须重写hashCode方法和equals方法。
同名、同年龄,视为一人,只能存储一次。
*/
class Student{
private String name;
private int age;
public Student(){
}
public Student(String name , int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
@Override
public String toString(){
return "Student{ "+"name = '"+name+'\''+",age = " + age+'}';
}
}
public class HashSetSaveStudent{
// 创建hash集合存储Student
HashSet<Student> set = new HashSet<>();
Student s1 = new Student("袁睿昕",21);
Student s2 = new Student("袁睿昕",21);
Student s3 = new Student("袁睿昕",20);
set.add(s1);
set.add(s2);
set.add(s3);
System.out.println(set);
//输出:{Student{name = '袁睿昕',age = 21},name = '袁睿昕',age = 21},name = '袁睿昕',age = 20}}
// 因为没有重写hashCode方法,so,hashCode不同。
// so,要添加hashCode和equals方法
@Override
public boolean equals(object o){
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
Student st = (Student) o;
return age == st.age && Object.equals(name,st.name);
}
@Override
public int hashCode(){
return Object.hash(name,age);
}
// 添加完以上两个方法后,再次执行结果
// 输出:{name = '袁睿昕',age = 21},name = '袁睿昕',age = 20}}
}
7.6 、 LinkedHashSet (有序、不允许重复)
我们知道HashSet保证元素唯一,可是元素放进去是没有顺序的,在HashSet下面有一个子类 java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。
演示代码如下:
public class LinkedHashSetDemo{
public static void main(String args[]){
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("def");
Iterator it = set.iterator();
while(it.hashNext()){
System.out.println(it.next());
}
}
}
运行结果:
bbb
aaa
abc
def