3.1 Set接口

  • Set接口是Collection的子接口,Set接口没有提供额外的方法
  • 存储的元素是无序,不可重复的.
  • 无序性:不是随机性,是指元素在底层存储的位置是无序,按照一定的方法来确定顺序的.
  • 不可重复性:不能像Set中添加相同的元素,也添加不进去
  • Set判断两个对象是否相同,不是使用==运算符,而是根据对象的boolean equals()方法判断

Set要求

实现类包括HashSet、LinkedHashSet、TreeSet

  • 添加到Set中的元素所在类,一定要重写equals()、hashCode()方法
  • 当向Set添加元素时,首先调用此对象所在类的hashCode()方法,计算此对象的哈希值,此哈希值决定了此对象在Set中的存储位置. 若此位置还没有存储对象,则此对象直接存储到这个位置. 若这个位置有存储了对象,那么在通过调用该对象的equals()方法比较这两个对象是否相同,如果equals()返回false则添加后面这个元素到Set中,否则不添加
  • hashCode()方法与equals()方法返回值方向要求一致.即返回表示相同或是不相同

3.2 HashSet

  • HashSet时Set接口的主要实现类,大多数时候都是使用这种
  • 使用Hash算法来存储集合中的元素,具有较好的存取、查找性能
  • 存储顺序、遍历顺序与元素插入顺序不同
  • 线程不安全
  • 集合元素可以是null
  • 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置
  • HashSet 集合判断两个元素相等的标准:两个对象通过int hashCode()方法比较相等,并且两个对象的boolean equals() 方法返回值也相等

HashSet 类的常用构造方法重载形式如下。

  • HashSet():构造一个新的空的 Set 集合。
  • HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示 HashSet 的父类,即指明该 Set 集合中存放的集合元素类型。c 表示其中的元素将被存放在此 Set 集合中。
    1. HashSet hs = new HashSet(); // 调用无参的构造函数创建HashSet对象
    2. HashSet<String> hss = new HashSet<String>(); // 创建泛型的 HashSet 集合对象

hashCode()方法

  • 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
  • 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则
  • 重写 hashCode() 方法的基本原则
    • 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值
    • 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等
    • 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值

用法示例:

  1. public class Student {
  2. private String name;
  3. private int age;
  4. public Student() {
  5. }
  6. public Student(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public int getAge() {
  17. return age;
  18. }
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22. @Override
  23. public boolean equals(Object o) {
  24. if (this == o)
  25. return true;
  26. if (o == null || getClass() != o.getClass())
  27. return false;
  28. Student student = (Student) o;
  29. return age == student.age &&
  30. Objects.equals(name, student.name);
  31. }
  32. @Override
  33. public int hashCode() {
  34. return Objects.hash(name, age);
  35. }
  36. }
public class HashSetDemo2 {
    public static void main(String[] args) {
        //创建集合对象   该集合中存储 Student类型对象
        HashSet<Student> stuSet = new HashSet<Student>();
        //存储 
        Student stu = new Student("于谦", 43);
        stuSet.add(stu);
        stuSet.add(new Student("郭德纲", 44));
        stuSet.add(new Student("于谦", 43));
        stuSet.add(new Student("郭麒麟", 23));
        stuSet.add(stu);

        for (Student stu2 : stuSet) {
            System.out.println(stu2);
        }
    }
}
执行结果:
Student [name=郭德纲, age=44]
Student [name=于谦, age=43]
Student [name=郭麒麟, age=23]

参考:https://github.com/veigaran/java/blob/master/day13/src/com/java/www/HashSetTest.java

3.3 LinkedHashSet

适用场景:频繁的遍历,较少的插入、删除

简单的说就是可以保证元素按插入的顺序排列的set集合

  • LinkedHashSet是HashSet的子类
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能
  • LinkedHashSet 不允许集合元素重复
  • addAll(Set obj) 在当前集合最后添加obj集合中的元素,添加顺序按照obj集合的顺序
    public class LinkedHashSetDemo {
     public static void main(String[] args) {
         Set<String> set = new LinkedHashSet<String>();
         set.add("bbb");
         set.add("aaa");
         set.add("abc");
         set.add("bbc");
         Iterator<String> it = set.iterator();
         while (it.hasNext()) {
             System.out.println(it.next());
         }
     }
    }
    结果:
    bbb
    aaa
    abc
    bbc
    

用法参考:https://github.com/veigaran/java/blob/master/day13/src/com/java/www/LinkedHashSetTest.java

3.4 TreeSet

TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。

TreeSet 只能对实现了 Comparable 接口的类对象进行排序

因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。

表 1 列举了 JDK 类库中实现 Comparable 接口的类,以及这些类对象的比较方式。

比较方式
包装类(BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short) 按数字大小比较
Character 按字符的 Unicode 值的数字大小比较
String 按字符串中字符的 Unicode 值的数字大小比较

TreeSet 类除了实现 Collection 接口的所有方法之外,还提供了如表 2 所示的方法。

方法名称 说明
E first() 返回此集合中的第一个元素。其中,E 表示集合中元素的数据类型
E last() 返回此集合中的最后一个元素
E poolFirst() 获取并移除此集合中的第一个元素
E poolLast() 获取并移除此集合中的最后一个元素
SortedSet subSet(E fromElement,E toElement) 返回一个新的集合,新集合包含原集合中 fromElement 对象与 toElement 对象之间的所有对象。包含 fromElement 对象,不包含 toElement 对象
SortedSet headSet<E toElement〉 返回一个新的集合,新集合包含原集合中 toElement 对象之前的所有对象。 不包含 toElement 对象
SortedSet tailSet(E fromElement) 返回一个新的集合,新集合包含原集合中 fromElement 对象之后的所有对 象。包含 fromElement 对象

注意:表面上看起来这些方法很多,其实很简单。因为 TreeSet 中的元素是有序的,所以增加了访问第一个、前一个、后一个、最后一个元素的方法,并提供了 3 个从 TreeSet 中截取子 TreeSet 的方法
用法参考:https://github.com/veigaran/java/blob/master/day13/src/com/java/www/TreeSetTest.java