目录
Set概念
Set集合类似于一个罐子,程序可以依次把多个对象"丢进"Set集合,而Set集合通常不能记住元素的添加顺序。也就是说Set集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set集合中不能包含重复的对象,并且最多只允许包含一个null元素。
Set实现了Collection接口,它主要有两个常用的实现类:HashSet
类和 TreeSet
类
概念: 是一个不允许出现重复元素并且无序(访问顺序和插入顺序不一致)的集合(可以null)
Set没有添加额外的方法
Set主要包含三种存放数据类型的变量,分别是HashSet
、LinkedHash
、TreeSet
HashSet
类
特点:
1.不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化
2.HashSet
不是同步的,如果多个线程同时访问或修改一个HashSet
,则必须通过代码来保证其同步
3.集合元素值可以是null
HashSet
是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。HashSet
是按照Hash算法来存储集合中的元素。因此具有很好的存取和查找性能。
HashSet
集合的元素值可以是null
HashSet
是Set接口的经典实现,大多数时候使用Set集合时使用这个实现类。
HashSet
按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。底层数据结构是哈希表
HashSet
不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也可能发生变化
HashSet
不是线程安全的,如果多个线程尝试同时修改HashSet
,则最终结果是不确定的。必须在多线程访问时显式同步对HashSet
的并发访问
HashSet
内部存储机制
当向HashSet
集合中存入一个元素时,HashSet
会调用该对象的hashCode
方法来得到该对象的hashCode
值,然后根据该hashCode
值决定该对象在HashSet
中的存储位置。如果两个元素通过equals 方法比较为true,但它们的hashCode
方法返回的值为false,HashSet
将会把它们存储在不同位置,依然可以添加成功。
所以,HashSet
集合判断两个元素的标准时两个对象通过equals比较相等,并且两个对象的hashCode
方法的返回值也相等。
总结: 元素的哈希值时通过hashcode
方法来获取的,HashSet
首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法,如果equals为true,HashSet
就视为同一个元素。如果,为false就不是同一个元素。
实现原理: 数组Node<K,V>[]+链表(jdk1.7) 数组+链表/红黑树(jdk1.8)
1、根据hashCode()
方法计算出hash值,在进行一定运算,算出元素在数组中的存储位置---分组 2、如果此位置为null,就把此对象放进去
3、如果此位置已经有值,那会判断当前对象和此位置对象的hash值是否相同 如果相同,则继续判断equals方法 如果equals相同,则不存储 如果equals不相同,则以链表的方式存储 如果不相同,直接存储,以链表的方式 jdk1.8以后,如果链表上的对象超过8个,就以红黑树的方式存储,提高查询效率
如果自定义类集合需要不重复 我们就要重写自定义类的hashCode() equals()
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Baidu");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Baidu"); // 重复的元素不会被添加
System.out.println(sites);
}
equals 和 hashCode
Person类
public class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
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 "Person [name=" + name + ", age=" + age + "]";
}
调试类
public class TestHashSet {
public static void main(String[] args) {
HashSet<Integer> set=new HashSet<Integer>();
set.add(12);
set.add(23);
set.add(23);
System.out.println(set);//输出结果为: [23,12]
System.out.println(set.contains(12));//判断是否含有12
set.remove(23);//移除23
set.clear();//清除所有元素
System.out.println(set);
Iterator<Integer> it=set.iterator();
while(it.hasNext()) { //无法获取单个元素//可以遍历
System.out.println(it.next());
}
set.remove(12);
set.add(20); //在Set不能修改元素,只能先添再删,或者先删再添
System.out.println(set);
System.out.println("通话".hashCode());
System.out.println("重地".hashCode());//hashCode相等
HashSet<Person> pSet=new HashSet<Person>();
Person p1=new Person("zhangsan",21);
Person p2=new Person("lisi",21);
Person p3=new Person("lisi",16);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(p3.hashCode());
pSet.add(p1);
pSet.add(p2);
pSet.add(p3);
System.out.println(pSet);
//输出为[Person [name=lisi, age=16], Person [name=zhangsan, age=21], Person [name=lisi, age=21]]
}
}
LinkedHashSet
LinkedHashSet
是Set集合的一个实现,具有Set集合不重复的特点,同时具有可预测的迭代顺序(插入顺序)。维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,保证访问顺序和插入顺序一致
LinkedHashSet
是一个非线程安全的集合。
TreeSet
TreeSet
是一个有序的集合,它的作用是提供有序的Set集合。它继承于AbstractSet
抽象类,实现了NavigableSet<E>, Cloneable, java.io.Serializable
接口。
底层实现: 红黑树
使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator 进行排序,具体取决于使用的构造方法。
根据Comparable接口中compareTo
方法来判断元素是否重复,不是equals()。但是一个类中同时有这两个方法时,相等的逻辑要一致。
总结
看到array,就要想到角标。
看到link,就要想到first,last。
看到hash,就要想到hashCode,equals.
看到tree,就要想到两个接口。Comparable,Comparator。