Set集合

作者: luoqiang108 | 来源:发表于2018-03-05 21:13 被阅读0次
    • Set不重复集,无序。
    • Set不能通过下标获取指定的元素。因为所以没有下标。可以使用Iterator的方式迭代集合。若我们把List看成是有许多格子的盒子,那么Set就好像一个袋子。
    • 常用实现类:
      HashSet:使用散列算法(哈希)实现Set集合。
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    /**
     * Set集合 无序且不重复集
     * 常用实现类:
     *      HashSet:使用散列算法(哈希)实现Set集合
     */
    public class SetTest {
        public static void main(String[] args) {
            //实例化一个HashSet集合
            Set<String> set = new HashSet<String>();
            /**
             * 向集合中添加元素也使用add
             * 但是add方法不是向集合末尾追加元素,因为无序
             */
            set.add("One");
            set.add("Two");
            set.add("Three");
            /**
             * Set集合没有get(int index)方法
             * 我们不能像使用List那样,根据下标获取元素。
             * 想获取元素需要使用Iterator
             */
            Iterator<String> it = set.iterator();
            while (it.hasNext()){
                String element = it.next();
                System.out.println(element);
            }
            /*
            * 宏观上讲:元素的顺序和存放顺序是不同的。
            * 但是在内容不变的前提下,存放顺序是相同的(使用相同的算法)。
            * 但是我们使用的时候,要当做是无序的使用。
            * */
            //使用For-Each循环遍历Set集合
            for (String element:set){
                System.out.println(element);
            }
        }
    }
    

    hashCode()方法

    • hashCode()方法是Object定义的方法。所以每个类都会有该方法。若我们定义的类重写了equals()方法,就要重写hashCode()方法(Object提供的hashCode方法将返回该对象所在内存地址的整数形式)。
    • 若equals方法返回true,那么这两个对象应该有相同的hashCode值,反过来不是必须的,当时最好是这样。可以提高诸如HashSet这样的数据结构的效率,一般情况下可以使用IDE提供的工具自动生成hashCode方法。

    HashSet和hashCode方法的关系

    • HashSet是Set接口的实现类,通过hash表的方式实现;在将对象加入HashSet集合中时,需要获取对象的hashCode值通过hash算法索引到对应的存储空间。
    • 进行hash算法确定存储空间,当通过contains方法判断Set集合中是否包含某个对象是,需要首先根据对象的hashCode值索引到特定的空间,然后再和空间中的对象调用equals方法进行比较,这种查找方式不同于线性表的逐个比较,有很高的效率。
    import java.util.HashSet;
    import java.util.Set;
    /**
     * hashCode
     */
    public class HashTest {
        public static void main(String[] args) {
            Set<MyPoint> set = new HashSet<MyPoint>();
            set.add(new MyPoint(1,2));
            set.add(new MyPoint(3,4));
            /**
             * 查看新创建的对象是否在set中被包含
             * 虽然这里是新创建的对象,但是通过散列算法找到了位置后
             * 和里面存放的元素进行equals比较为true,所以依然认为
             * 是被包含的。
             */
            System.out.println(set.contains(new MyPoint(1,2)));//true
        }
    }
    class MyPoint{
        private int x;
        private int y;
        public MyPoint(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        /**
         *equals()和hashCode()方法我们一般让IDE帮我们自动生成
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyPoint point = (MyPoint) o;
            if (x != point.x) return false;
            return y == point.y;
        }
        /**
         * 若我们不重写hashCode,那么使用的就是Object提供的,
         * 该方法是返回句柄(对象存放地址)!换句话说,不同的对象hashCode不同
         * @return
         */
        @Override
        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            return result;
        }
    }
    

    equals()方法和hashCode()方法对HashSet的影响

    import java.util.HashSet;
    import java.util.Set;
    public class HashTest {
        public static void main(String[] args) {
            Set<MyPoint> set1 = new HashSet<MyPoint>();
            MyPoint p1 = new MyPoint(1,2);
            MyPoint p2 = new MyPoint(1,2);
            System.out.println("p1和p2是否为同一个对象"+(p1==p2));
            //重写equals方法和hashCode方法时的结果:false
            //重写equals方法未重写hashCode方法时的结果:false
            //未重写equals方法重写hashCode方法时的结果:false
            System.out.println("p1和p2内容是否一样"+p1.equals(p2));
            //重写equals方法和hashCode方法时的结果:true
            //重写equals方法未重写hashCode方法时的结果:true
            //未重写equals方法重写hashCode方法时的结果:false
            System.out.println("p1和p2的hashCode是否一样"+(p1.hashCode()==p2.hashCode()));
            //重写equals方法和hashCode方法时的结果:true
            //重写equals方法未重写hashCode方法时的结果:false
            //未重写equals方法重写hashCode方法时的结果:true
            set1.add(p1);
            set1.add(p2);
            System.out.println("hashset集合的元素数"+set1.size());
            //重写equals方法和hashCode方法时的结果:1
            //重写equals方法未重写hashCode方法时的结果:2
            //未重写equals方法重写hashCode方法时的结果:2
            System.out.println(set1);
            //重写equals方法和hashCode方法时的结果:[MyPoint{x=1, y=2}]
            //重写equals方法未重写hashCode方法时的结果:[MyPoint{x=1, y=2}, MyPoint{x=1, y=2}]
            //未重写equals方法重写hashCode方法时的结果:[MyPoint{x=1, y=2}, MyPoint{x=1, y=2}]
            /**
             * 当我们重写了Point的equals方法和hashCode方法后我们发现
             * 虽然p1和p2是两个对象,但是当我们将它们同时放入集合中时,
             * p2对象并没有被添加进集合。因为p1在放入后,p2放入时根据
             * p2的hashCode计算的位置中p2与该位置的p1的equals比较为true,
             * HashSet认为该对象已经存在,所有拒绝将p2存入集合。
             */
            /**
             * 若我们不重写MyPoint的hashCode方法,但是重写了equals方法。
             * 两个对象都可以放入HashSet集合中,因为两个对象具有不同的
             * hashCode值,那么当它们在放入集合时通过hashCode值进行
             * 散列算法结果就不相同,那么它们会被放入集合的不同位置。
             * 位置不相同,HashSet则认为他们不同,所有它们可以全部
             * 被存入集合。
             */
            /**
             * 若我们重写了hashCode但是不重写equals方法,
             * hashCode相同的情况下,在存放元素时,它们会在相同的位置,
             * HashSet会在相同的位置上将后放入的对象与该位置其它对象依次
             * 进行equals比较,若不相同,则将其存入。
             * 在同一个位置若干元素,这些元素会被放入一个链表中。
             * 由此可以看出。我们应该尽量使得所有类的不同对象的hashCode
             * 值不同。这样才可以提高HashSet在检索元素上的效率!
             * 否则可能检索效率还不如List。
             */
            /**
             * 结论:
             *      使用HashSet集合存放元素时
             *      应保证equals与hashCode方法在api上定义的要求
             * 存放规则:
             *      不同对象存放时,不会存放hashCode相同(存放位置相同)并且equals相同
             *      (内容相同)的
             *      对象。缺一不可,否则HashSet不认为它们是重复对象。
             */
        }
    }
    class MyPoint{
        private int x;
        private int y;
        public MyPoint(int x, int y) {
            this.x = x;
            this.y = y;
        }
        @Override
        public String toString() {
            return "MyPoint{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }
    
        /**
         *equals()和hashCode()方法我们一般让IDE帮我们自动生成
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyPoint point = (MyPoint) o;
            if (x != point.x) return false;
            return y == point.y;
        }
        /**
         * 若我们不重写hashCode,那么使用的就是Object提供的,
         * 该方法是返回句柄(对象存放地址)!换句话说,不同的对象hashCode不同
         * @return
         */
        @Override
        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            return result;
        }
    }
    

    相关文章

      网友评论

        本文标题:Set集合

        本文链接:https://www.haomeiwen.com/subject/qxphfftx.html