zoukankan      html  css  js  c++  java
  • 《疯狂Java讲义》(二十四)---- Set集合

    • HashSet类

      HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。当向HashSet集合中存入一个元素时,HashSet会调用对象的hashCode方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals方法返回true,但是hashCode方法返回不同的值,HashSet会把这两个对象存入不同的位置,不会把它们当作同一个对象。

    package com.ivy.collection;
    
    import java.util.HashSet;
    
    class A {
        public boolean equals(Object obj) {
            return true;
        }
    }
    
    class B {
        public int hashCode() {
            return 1;
        }
    }
    
    class C {
        public boolean equals(Object obj) {
            return true;
        }
        
        public int hashCode() {
            return 2;
        }
    }
    public class HashSetDemo {
    
        public static void main(String[] args) {
            HashSet set = new HashSet();
            set.add(new A());
            set.add(new A());
            set.add(new B());
            set.add(new B());
            set.add(new C());
            set.add(new C());
            System.out.println(set);
    
        }
    
    }

    输出结果

    [com.ivy.collection.B@1, com.ivy.collection.B@1, com.ivy.collection.C@2, com.ivy.collection.A@e3f6d, com.ivy.collection.A@a4b78b]

    两个A,两个B是不同的,但是两个C是同一个对象。

    所以,当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals方法,也应该重写其hashCode方法。其规则是:如果两个对象通过equals方法比较返回true,那这两个对象的hashCode值也应该相同。对象中用作equals方法比较标准的Field,都应该用来计算hashCode值。

    当向HashSet中添加可变对象时,必须十分小心,如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法准确访问该对象。

    • LinkedHashSet类

      LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet。

    • TreeSet类

      TreeSet可以确保机荷元素处于排序状态。TreeSet支持两种排序方法:自然排序和定制排序。

    1. 自然排序

        TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排序。Java提供了一个Comparable接口,该接口中定义了一个compareTo(Object obj)方法,实现了该接口的类的对象就可以比较大小。如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序将会抛出异常。

        对于TreeSet而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo方法比较是否返回0,如果返回0,TreeSet会认为它们相等,不会被添加进TreeSet。

        例如:

        向TreeSet添加两个SomeClass对象A和B,添加A时,因为TreeSet中没有任何元素,所以可以添加进去,但是当添加B时,TreeSet会调用B.compareTo(Object obj)与TreeSet中其他元素比较,如果SomeClass没有实现Comparable接口,则会引发ClassCastException。

        当需要把一个对象放入TreeSet中,重写该对象对应类的equals方法时,应保证该方法与compareTo方法有一致的结果,其规则是:如果两个对象通过equals方法比较返回true时,这两个对象通过compareTo方法比较应该返回0。

        推荐HashSet和TreeSet中只放入不可变对象,因为如果向TreeSet中添加一个可变对象,并且后面程序修改了该对象的Field,这将导致它与其他对象的大小发生改变,但TreeSet不会再次调整它们的顺序,甚至可能导致TreeSet中保存的这两个对象通过compareTo方法比较返回0。

      2. 定制排序

        如果需要实现定制排序,需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。Comparator接口包含一个int compare(Object o1, Object o2)方法,用于比较o1和o2的大小。

    import java.util.Comparator;
    import java.util.TreeSet;
    
    class M {
        int age;
        public M(int age) {
            this.age = age;
        }
        public String toString() {
            return "M[age:" + age + "]";
        }
    }
    public class TreeSetDemo {
    
        public static void main(String[] args) {
            TreeSet ts = new TreeSet(new Comparator() {
                public int compare(Object o1, Object o2){
                    M m1 = (M)o1;
                    M m2 = (M)o2;
                    return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0;
                }
            });
    
            ts.add(new M(5));
            ts.add(new M(-3));
            ts.add(new M(9));
            System.out.println(ts);
        }
    
    }

    输出结果:

    [M[age:9], M[age:5], M[age:-3]]

    • EnumSet类

      EnumSet没有提供构造器来创建EnumSet,程序应该通过它提供的static方法来创建。例如:

    import java.util.Collection;
    import java.util.EnumSet;
    import java.util.HashSet;
    
    enum Season {
        SPRING, SUMMER, FALL, WINTER
    }
    public class EnumSetDemo {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            EnumSet es1 = EnumSet.allOf(Season.class);
            System.out.println(es1);
            
            EnumSet es2 = EnumSet.noneOf(Season.class);
            System.out.println(es2);
            
            es2.add(Season.WINTER);
            es2.add(Season.SPRING);
            System.out.println(es2);
            
            EnumSet es3 = EnumSet.of(Season.SUMMER, Season.WINTER);
            System.out.println(es3);
            
            EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTER);
            System.out.println(es4);
            
            EnumSet es5 = EnumSet.complementOf(es4);
            System.out.println(es5);
            
            Collection c = new HashSet();
            c.clear();
            c.add(Season.FALL);
            c.add(Season.SPRING);
            EnumSet es6 = EnumSet.copyOf(c);
            System.out.println(es6);
            c.add("Java");
            c.add("C#");
            es6 = EnumSet.copyOf(c);
        }
    
    }

    输出结果:

    [SPRING, SUMMER, FALL, WINTER]
    []
    [SPRING, WINTER]
    [SUMMER, WINTER]
    [SUMMER, FALL, WINTER]
    [SPRING]
    [SPRING, FALL]
    Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Enum
    at java.util.EnumSet.copyOf(EnumSet.java:168)
    at com.ivy.collection.EnumSetDemo.main(EnumSetDemo.java:41)

    最后一行抛出异常,原因是当复制Collection集合中的所有元素来创建EnumSet时,要求Collection集合中的所有元素必须是同一个枚举类的枚举值。

    • 各Set实现类的性能分析

       HashSet vs TreeSet

      HashSet的性能总是比TreeSet好,因为TreeSet需要额外的红黑树算法来维护集合次序。

      只有当需要一个保持排序的Set时,才应该使用TreeSet.

      HashSet vs LinkedHashSet

      对于普通的插入删除操作,LinkedHashSet比HashSet要慢,这是由维护链表所带来的额外开销造成的。

      但因为有了链表,遍历LinkedHashSet会更快。

      EnumSet是所有set中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

      Set的三个实现类HashSet/TreeSet/EnumSet都是线程不安全的。通常可以通过如下方法来包装Set集合,手动保证该Set集合的同步性:

      SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

  • 相关阅读:
    服务器学习
    第七章节 排序
    第六章节 优先队列/堆
    用cmd运行java时的问题
    转载一下如何联系口语
    在翻译群里看到的一个句子的翻译
    Android EditText的设置(转)
    荐 android 如何打包自定义控件(转)
    Android 自定义 SpinnerButton(转)
    Android PopupWindow的使用技巧(转)
  • 原文地址:https://www.cnblogs.com/IvySue/p/6376653.html
Copyright © 2011-2022 走看看