Set
接口:存储无序的,不可重复的数据
HashSet
:Set的主要实现类;线程不安全;可以存储null
值
LinkedHashSet
:HashSet
的子类;遍历其内部数据时,可以按添加的顺序遍历
TreeSet
:可以按照添加的元素的指定属性进行排序。
Set
接口中没有额外新定义方法,使用的都是Collection
中的方法。
要求:向Set中添加的数据,其所在的类一定要重写hashCode()
和equals()
重写的hashCode()
和equals()
方法尽可能保持一致:相等的对象必须具有相等的散列码
* 重写两个方法的技巧:对象中用作equals()
方法比较的Field
,都应该用来计算hashCode
值
Set:存储无需的、不可重复的数据<br /> 1、无序性:<br /> * 无序性不等于随机性。存储的数据在数组中并非按照数组索引顺序添加,而是根据数据的哈希值决定的<br /> 2、不可重复性:<br /> * 添加的元素按照`equals()`判断时,不能返回`true`,即相同的元素只能添加一个。<br /> *
添加元素的过程:以HashSet为例:<br /> 向`HashSet`中添加元素a,首先调用元素a所在类的`HashCode()`方法,计算元素a的哈希值,<br /> 此哈希值通过某种算法计算出在`HashSet`底层数组中的存放位置(即为索引位置),判断数组<br /> 此位置上是否为`null`:<br /> 1) 如果为`null`,则元素a添加成功。<br /> 如果此位置上有其他元素b,比较两者哈希值<br /> 2)如果哈希值不同,则添加成功(链表的形式)<br /> 3)如果哈希值相同,则调用元素a所在类的`equals()`方法:<br /> `equals()`方法返回`ture`,则添加失败,反之添加成功。
对于添加成功的情况2和情况3而言:元素a与已经存在指定所应位置上的数据以链表的方式进行添加。<br /> 存储。<br /> 在JDK7中,元素a放到数组中,指向原来的元素;<br /> 在JDK8中,原来的元素在数组中,指向元素a。<br /> */
HashSet测试代码:
package com.SetTest;
import com.CollectionTest.Person;
import org.junit.Test;
import java.util.*;
public class SetTest {
@Test
public void test1(){
Set set = new LinkedHashSet();
set.add(456);
set.add(123);
set.add("AA");
set.add("CC");
set.add(new Person("Tom",12));
set.add(new User("Tom",12));
set.add(new User("Tom",12));
set.add(129);
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class User{
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;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
注:如果不重写hashCode()
方法,那么equals()
方法会直接去调两个对象的地址值,显然两个分别实例化的对象的地址值是不同的,所以就会添加成功。
LinkedHashSet()
:
LinkedHashSet底层的存储结构:
双向链表和随机储存结合。
这样设计是为了应对频繁的遍历操作
HashSet
重要例题:
public void test2(){
HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);//这里Person类重写了equals()和hashCode()方法之后
//对象实际得索引位置变了,但是存在set里的索引还没变,所以删除失败
set.add(new Person(1001,"CC"));//HashSet中p1对象的实际位置是空的,所以
//能加进去
System.out.println(set);
set.add(new Person(1001,"AA"));//这里把这个对象给链进去了
System.out.println(set);
}