Set接口:存储无序的,不可重复的数据
    HashSet:Set的主要实现类;线程不安全;可以存储null
    LinkedHashSetHashSet的子类;遍历其内部数据时,可以按添加的顺序遍历
    TreeSet:可以按照添加的元素的指定属性进行排序。

    Set接口中没有额外新定义方法,使用的都是Collection中的方法。

    要求:向Set中添加的数据,其所在的类一定要重写hashCode()equals()
    重写的hashCode()equals()方法尽可能保持一致:相等的对象必须具有相等的散列码
    * 重写两个方法的技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode

    1. Set:存储无需的、不可重复的数据<br /> 1、无序性:<br /> * 无序性不等于随机性。存储的数据在数组中并非按照数组索引顺序添加,而是根据数据的哈希值决定的<br /> 2、不可重复性:<br /> * 添加的元素按照`equals()`判断时,不能返回`true`,即相同的元素只能添加一个。<br /> *
    2. 添加元素的过程:以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`,则添加失败,反之添加成功。
    3. 对于添加成功的情况2和情况3而言:元素a与已经存在指定所应位置上的数据以链表的方式进行添加。<br /> 存储。<br /> JDK7中,元素a放到数组中,指向原来的元素;<br /> JDK8中,原来的元素在数组中,指向元素a。<br /> */

    HashSet测试代码:

    1. package com.SetTest;
    2. import com.CollectionTest.Person;
    3. import org.junit.Test;
    4. import java.util.*;
    5. public class SetTest {
    6. @Test
    7. public void test1(){
    8. Set set = new LinkedHashSet();
    9. set.add(456);
    10. set.add(123);
    11. set.add("AA");
    12. set.add("CC");
    13. set.add(new Person("Tom",12));
    14. set.add(new User("Tom",12));
    15. set.add(new User("Tom",12));
    16. set.add(129);
    17. Iterator iterator = set.iterator();
    18. while(iterator.hasNext()){
    19. System.out.println(iterator.next());
    20. }
    21. }
    22. }
    23. class User{
    24. private String name;
    25. private int age;
    26. public User() {
    27. }
    28. public User(String name, int age) {
    29. this.name = name;
    30. this.age = age;
    31. }
    32. public String getName() {
    33. return name;
    34. }
    35. public int getAge() {
    36. return age;
    37. }
    38. public void setName(String name) {
    39. this.name = name;
    40. }
    41. public void setAge(int age) {
    42. this.age = age;
    43. }
    44. @Override
    45. public String toString() {
    46. return "User{" +
    47. "name='" + name + '\'' +
    48. ", age=" + age +
    49. '}';
    50. }
    51. @Override
    52. public boolean equals(Object o) {
    53. if (this == o) return true;
    54. if (o == null || getClass() != o.getClass()) return false;
    55. User user = (User) o;
    56. return age == user.age && Objects.equals(name, user.name);
    57. }
    58. @Override
    59. public int hashCode() {
    60. return Objects.hash(name, age);
    61. }
    62. }

    注:如果不重写hashCode()方法,那么equals()方法会直接去调两个对象的地址值,显然两个分别实例化的对象的地址值是不同的,所以就会添加成功。

    LinkedHashSet():
    LinkedHashSet底层的存储结构:
    image.png双向链表和随机储存结合。

    这样设计是为了应对频繁的遍历操作

    HashSet重要例题:

    1. public void test2(){
    2. HashSet set = new HashSet();
    3. Person p1 = new Person(1001,"AA");
    4. Person p2 = new Person(1002,"BB");
    5. set.add(p1);
    6. set.add(p2);
    7. p1.name = "CC";
    8. set.remove(p1);
    9. System.out.println(set);//这里Person类重写了equals()和hashCode()方法之后
    10. //对象实际得索引位置变了,但是存在set里的索引还没变,所以删除失败
    11. set.add(new Person(1001,"CC"));//HashSet中p1对象的实际位置是空的,所以
    12. //能加进去
    13. System.out.println(set);
    14. set.add(new Person(1001,"AA"));//这里把这个对象给链进去了
    15. System.out.println(set);
    16. }