Set继承于Collection接口,是一个不允许出现重复元素,并且无序的集合,主要有HashSet和TreeSet两大实现类。
在判断重复元素的时候,Set集合会调用hashCode()和equal()方法来实现。
HashSet是哈希表结构,主要利用HashMap的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置;
TreeSet是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序;
Set集合框架结构:
1.1 Set常用方法
与List接口一样,Set接口也提供了集合操作的基本方法。
但与List不同的是,Set还提供了equals(Object o)和hashCode(),供其子类重写,以实现对集合中插入重复元素的处理;
public interface Set<E> extends Collection<E> {
// 添加功能
boolean add(E e);
boolean addAll(Collection<? extends E> c);
// 删除功能
boolean remove(Object o);
boolean removeAll(Collection<?> c);
void clear();
//长度功能
int size();
//判断功能
boolean isEmpty();
boolean contains(Object o);
boolean containsAll(Collection<?> c);
boolean retainAll(Collection<?> c);
//获取Set集合的迭代器:
Iterator<E> iterator();
//把集合转换成数组
Object[] toArray();
<T> T[] toArray(T[] a);
//判断元素是否重复,为子类提高重写方法
boolean equals(Object o);
int hashCode();
}
HashSet实现Set接口,底层由HashMap(后面讲解)来实现,为哈希表结构,新增元素相当于HashMap的key,value默认为一个固定的Object。
有元素插入的时候,会计算元素的hashCode值,将元素插入到哈希表对应的位置中来;
它继承于AbstractSet,实现了Set, Cloneable, Serializable接口。
(1)HashSet继承AbstractSet类,获得了Set接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了AbstractCollection类;
(2)HashSet实现了Set接口,获取Set接口的方法,可以自定义具体实现,也可以继承AbstractSet类中的实现;
(3)HashSet实现Cloneable,得到了clone()方法,可以实现克隆功能;
(4)HashSet实现Serializable,表示可以被序列化,通过序列化去传输,典型的应用就是hessian协议。
具有如下特点:
- 不允许出现重复因素;
- 允许插入Null值;
- 元素无序(添加顺序和遍历顺序不一致);
- 线程不安全,若2个线程同时操作HashSet,必须通过代码实现同步;
1.3 HashSet基本操作
HashSet底层由HashMap实现,插入的元素被当做是HashMap的key,根据hashCode值来确定集合中的位置,由于Set集合中并没有角标的概念,所以并没有像List一样提供get()方法。当获取HashSet中某个元素时,只能通过遍历集合的方式进行equals()比较来实现;
import java.util.Objects;
public class Student_Set {
private String name;
private int age;
public Student_Set() {
}
public Student_Set(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 boolean equals(Object o) {
if(this==o) return true;
if(o==null || getClass()!=o.getClass()) return false;
Student_Set student=(Student_Set) o;
return age==student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name,age);
}
public String toString() {
return "Student{name="+name+",age="+age+"}";
}
}
public class A01_HashSetDemo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
/*
哈希值:
对象的整数表现形式:
1.如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
2.如果已经重写hashCode方法,不同对象只要属性值相同,计算出的哈希值就是一样的
3.但是在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
*/
//1.创建对象
Student s1=new Student("张三",23);
Student s2=new Student("张三",23);
//2.如果已经重写hashCode方法,不同对象只要属性值相同,计算出的哈希值就是一样的
//3.但是在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
//在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
System.out.println("abc".hashCode());//96354
System.out.println("abc".hashCode());//96354
}
}