一、存储的数据特点
存储无序的、不可重复的数据——> 类似于:“高中数学中的集合”
二、元素添加过程
以HashSet为例
我们向HashSet中添加元素a,先调用元素a所在类的hashCode()方法,计算出元素a的hash值。
计算出的hash值再通过某种算法计算出元素a在HashSet中存储的位置(即索引位置)。判断该索引上是否已经元素:
如果此位置上没元素,那么元素a添加成功。 ————>情况1
如果此位置上已元素b(或已有以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。 ————>情况2
如果hash值相同,则调用元素a所在类的equals()方法:
equals()返回true,则添加失败。
equals()返回false,则添加成功。 ————>情况3
说明:对于添加成功的情况2和情况3:元素a与已在索引位置上存在的元素以链表的形式存储。
jdk 7:元素a放到数组中,并且指向原来的元素b。
jdk 8:原来的元素b仍在此位置,并且指向元素a。
俗称:七上八下
三、常用方法
Set接口中没有再定义方法,即使用的都是Collection中声明的方法。
四、常用实现类
1、Collection接口:单列集合,存储一个一个的对象
1.2、Set接口:存储无序的、不可重复的数据——> “数学中的集合”
HashSet:作为Set接口的主要实现类,线程不安全;可以存储null值。 |
LinkedHashSet:作为HashSet的子类,存储数据时,在HashSet的基础上添加了一对引用,用来指向前一个数据和后一个数据。在遍历其内部元素时,可以照添加元素顺序进行遍历。 优点:对于频繁的遍历操作,比HashSet更加高效。 |
TreeSet:可以照添加对象的指定属性,进行排序。 |
五、存储对象所在类的要求
要求:
1、添加进HashSet(以HashSet和LinkedHashSet为例)中的数据,其所在的类必须要重写HashCode()与equals()方法。
2、重写的HashCode()与equals()方法要保持一致性:相等的对象必须具有相等的散列码。
重写技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
六、TreeSet的使用
1 、使用说明:
①添加进TreeSet的元素,必须是同一个类的对象。否则会报错:ClassCastException
②两种排序方式:自然排序(实现Comparable接口) 和 定制排序(实现Comparator接口)
2、常用的排序方式:
①自然排序
自定义排序方式:姓名从大到小,并且年龄从小到大。
public class User implements Comparable {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public boolean equals(Object o) {
System.out.println("User.equals()....");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return Objects.equals(name, user.name);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
//按照姓名从大到小,年龄从小到大
@Override
public int compareTo(Object o) {
if(o instanceof User){
User user = (User) o;
// return -this.name.compareTo(user.name);
int compare = -this.name.compareTo(user.name);
if(compare != 0){
return compare;
}else{
return Integer.compare(this.age,user.age);
}
}
throw new RuntimeException("数据类型不匹配");
}
}
测试代码:
@Test
public void test1(){
Set set = new TreeSet();
//ClassCastException
// set.add(123);
// set.add(456);
// set.add("aa");
// set.add(new User("Tom",12));
// set.add(12);
///举例二
set.add(new User("Tom",12));
set.add(new User("Jerry",23));
set.add(new User("Jack",51));
set.add(new User("Jim",6));
set.add(new User("Bob",61));
set.add(new User("Bob",23));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
运行结果:
注意:自然排序中,比较两个元素是否相同的标准是compareTo()方法返回0,不再是调用equals()方法比较。
②定制排序
自定义排序顺序:年龄由小到大。
@Test
public void test2(){
Comparator comparator = new Comparator() {
//按照年龄从小到大
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("数据类型不匹配");
}
}
};
Set set = new TreeSet(comparator);
set.add(new User("Tom",12));
set.add(new User("Jerry",23));
set.add(new User("Jack",51));
set.add(new User("Mary",6));
set.add(new User("Jim",6));
set.add(new User("Bob",61));
set.add(new User("Bob",23));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
运行结果: