Set接口
- 特点:无序、不允许重复,是Collection接口的子接口
- 没有定义新方法,所有的方法都是Collection接口中所定义的方法
实现类
- HashSet存储采用哈希表的方式进行存储,HashSet采用HashCode算法来存取集合中的元素,因此具有比较好的读取和查找性能
- LinkedHashSet是在HashSet的基础上添加一个额外的链表结构可以记录存储数据的顺序
- TreeSet采用的是树状结构进行数据存储
HashSet
- 底层实现方法:存储到Set中的所有数据最终都存储在一个HashMap中,其中存储的数据采用key的方式进行存储,值为PRESENT常量
常用算法
- boolean add(E e)向集合Set中添加元素,注意不保证顺序
- 同一个内容的对象,为什么没有出现覆盖的效果?(设置hashCode和equals方法的调用)
Set<Person> set=new HashSet<>();
Person p1=new Person(1L,"aa");
Person p2=new Person(1L,"aa");
System.out.println(p1==p2);//false,因为p1、p2是两个对象
set.add(p1);
set.add(p2);
System.out.println(set.size);//2
比对两个对象相等,调用流程为:
-
调用对象的hashcode方法,如果hashCode不相等则返回,认为两个对象不相等。
-
如果hash值相等则调用equals判断
-
潜规则要求:定义类时需要定义对应的hashCode和equals方法,要求:当equals为true时,hash值必须相等;当hash值相等时不一定equals为true
package come.day0307;
import java.util.HashSet;
import java.util.Set;
public class A{
public static void main(String args[]){
Set<Person> set=new HashSet<>();
Person p1=new Person(1L,"aa");
Person p2=new Person(1L,"aa");
System.out.println(p1==p2);//false,因为p1、p2是两个对象
set.add(p1);
set.add(p2);
System.out.println(set.size());//1
System.out.println(set);
}
}
class Person{
private long id;
private String name;
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
public Person() {}
public Person(long id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//输出
//false
//1
//[Person [id=1, name=aa]]
- boolean remove(Object o) 删除指定对象,同样需要hashCode和equals方法
- void clear()清空集合中的所有元素
- boolean contains(Object o)判断集合中是否有指定的对象,同样需要hashCode和equals方法
- int size()获取集合中的元素个数
Iterator<E> iterator()
用于遍历所存储的数据
Set<String> set = new LinkedHashSet<>();
set.add("abcd");
set.add("123");
Iterator<String> it=set.iterator();
while(it.hasNext()) {
String tmp=it.next();
System.out.println(tmp);
}
HashSet的特征
- 无序:不仅不能保证元素插入的顺序(如果需要顺序则可以使用LinkedHashSet),而且在元素在以后的顺序中也可能变化(这是由HashSet按HashCode存储对象(元素)决定的,对象变化则可能导HashCode变化)
- 如果需要访问的顺序和插入的顺序一致,可以使用HashSet的子类LinkedHashSet
- 不允许重复 [equals和hashcode]
- 结论:当HashSet判定对象重复时,首先调用的是对象的hashCode方法,如果两个对象的hashCode值相同时,才调用equals进行判定。如果hashCode值不相等则不会调用equals判断。如果hashcode相等而且equals为true,则后盖前。
- HashSet是线程非安全的,方法上没有同步约束
- HashSet元素值可以为NULL
LinkedHashSet
- LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素
- 在HashSet的基础上添加了一个链表结构记录存取的顺序
TreeSet
- TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合
- 在map中以key为需要存放的数据,以PERSENT常量为值存放数据
内部实现
- 底层是用TreeMap实现的,本质上是一个红黑树原理。正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(),
last(), lower(), higher(), subSet(), headSet(), tailSet()
**基本用法**
Set<Integer> set = new TreeSet<Integer>();
Random r = new Random();
for (int i = 0; i < 10; i++)
set.add(r.nextInt(100));
set.forEach(System.out::println);
TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。
public class A{
public static void main(String args[]){
Set<Person> set=new TreeSet<Person>();
Person p1=new Person(1L,"aa");
Person p2=new Person(1L,"aa");
set.add(p1);
set.add(p2);
System.out.println(set.size());
}
}
class Person{
private long id;
private String name;
public Person(long id, String name) {
super();
this.id = id;
this.name = name;
}
}
执行报错,原因是:添加到TreeSet中要求对象必须是可比较的
- 注意: 要求添加到TreeSet中的元素类型必须实现Comparable接口
public class A {
public static void main(String[] args) {
Set<Person> set=new TreeSet<Person>();
Person p1=new Person(1L,"aa");
Person p2=new Person(1L,"aa");
set.add(p1);
set.add(p2);
System.out.println(set.size());
System.out.println(p1==p2);
System.out.println(p1.equals(p2));
}
}
class Person implements Comparable<Person>{
private Long id;
private String name;
public Person(long id, String name) {
this.id=id;
this.name=name;
}
@Override
public int compareTo(Person o) {
//判空处理省略
int res=name.compareTo(o.name);
if(res==0) {
res=id.compareTo(o.id);
}
return res;
}
}
如果使用TreeSet时不会依靠hashcode和equals进行比较,相等性判断是依靠compareTo实现的