Java Set讲解

Java Set讲解

Set集合用于存放无序的,不重复的元素。

HashSet

HashSet类是Set接口的实现类,使用Hash值来决定集合内部存放的位置。
HashSet使用equals()对比元素,当equals()返回true,HashCode也相同,则不接受新元素,反之则允许其放入集合中。

//类A的equals方法总是返回true,但没有重写其hashCode()方法。
public class A {
    public boolean equals(Object obj) {
        return true;
    }
}
        HashSet books = new HashSet();
        books.add(new A());
        books.add(new A());
// 调用
while (aa.hasNext())
        {
            Object bb = aa.next();
            System.out.println(bb.getClass() + "====" + bb.hashCode());
        }
/** 运行结果:
class main.A====723074861
class main.A====895328852
*/

由上可知,HashSet在添加前对比两个对象时,equals返回了true,但因为HashCode值不同,所以新添加的元素被判定为2个不同的元素。

//类B的hashCode()方法总是返回1,但没有重写其equals()方法。
public class B {
        public int hashCode() {
            return 1;
        }
    }

books.add(new B());
books.add(new B());

/** 运行结果:
class main.B====1
class main.B====1
*/

HashSet在添加两个HashCode不同的元素时,由于equals返回了false,因此新添加的元素被判定为2个不同的元素。


//类C的hashCode()方法总是返回2,且有重写其equals()方法
public class C {
        public int hashCode() {
            return 2;
        }

        public boolean equals(Object obj) {
            return true;
        }
    }
books.add(new C());
books.add(new C());
/** 运行结果:
class main.C====2
*/

当equals和hashCode都相同时,HashSet才会将元素判定为同一个对象。


总结:

  1. hashCode()决定存放的位置,它和equals必须同时满足才允许新元素加入集合。

  2. 如果两个对象的hashCode相同,但是它们的equlas返回值不同,HashSet会在这个位置用链式结构来保存多个对象。而HashSet访问集合元素时也是根据元素的HashCode值来快速定位的,这种链式结构会导致性能下降。

  3. 使用HashSet集合,在重新元素内部equals和hashCode方法时,一定要知悉HashSet内部原理。

LinkedHashSet

LinkedHashSet继承自HashSet,顾名思义,LinkedHashSet在HashSet的基础上使用了链表的存储形式。基于此,我们大致应该猜想到,这种集合保持着顺序。

基于HashSet上的代码进行修改:

Set books = new LinkedHashSet();
        books.add(new A());
        books.add(new A());

        books.add(new B());
        books.add(new B());

        books.add(new C());
        books.add(new C());
        
        books.add("aaa");
        books.add("bbb");
        books.add("aaa");
        Iterator aa = books.iterator();
        while (aa.hasNext()) {
            Object bb = aa.next();
            System.out.println(bb.getClass() + "====" + bb.hashCode());
        }
/** 运行结果:
class main.A====723074861
class main.A====895328852
class main.B====1
class main.B====1
class main.C====2
class java.lang.String====96321
class java.lang.String====97314
*/

基于HashSet的知识分析Log可得出,A对象、B对象都被添加了两次,C对象由于被判定为同一个对象,因此不可重复插入。后面新增的 aaabbbaaa 中,前两个都被添加入了集合,最后一个元素又是 aaa ,因此没有存入到集合中。和HashSet()比较得知,LinkedHashSet是保持插入顺序的。


SortedSet

SortedSet是一个接口,该接口在Set接口上又实现了排序接口。

TreeSet
        TreeSet nums = new TreeSet();
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        System.out.println("默认进行了排序:" + nums);
        System.out.println("输出集合里的第一个元素:"+nums.first());
        System.out.println("输出集合里的最后一个元素:"+nums.last());
        System.out.println("返回小于4的子集,不包含4:"+nums.headSet(4));
        System.out.println("返回大于等于5的子集:"+nums.tailSet(5));
        System.out.println("返回大于等于-3,小于4的子集:"+nums.subSet(-3, 4));
/** 运行结果:
默认进行了排序:[-9, 2, 5, 10]
输出集合里的第一个元素:-9
输出集合里的最后一个元素:10
返回小于4的子集,不包含4:[-9, 2]
返回大于等于5的子集:[5, 10]
返回大于等于-3,小于4的子集:[2]
*/

TeeeSet不再使用HashCode值来决定元素的存储位置了,它采用红黑树的数据结构来存储集合元素。

TreeSet支持两种排序方式: 自然排序、定制排序

自然排序
  1. TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排序,即自然排序。如果试图把一个对象添加到TreeSet时,则该对象的类必须实现Comparable接口,否则程序会抛出异常。

  2. 当把一个对象加入TreeSet集合中时,TreeSet会调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树结构找到它的存储位置。如果两个对象通过compareTo(Object obj)方法比较相等,新对象将无法添加到TreeSet集合中(牢记Set是不允许重复的概念)。

  3. 当需要把一个对象放入TreeSet中,重写该对象对应类的equals()方法时,应该保证该方法与compareTo(Object obj)方法有一致的结果,即如果两个对象通过equals()方法比较返回true时,这两个对象通过compareTo(Object obj)方法比较结果应该也为0(即相等)

定制排序

若要实现自定义排序的效果,可以通过Comparator接口的帮助。该接口里包含一个int compare(T o1, T o2)方法,该方法用于比较大小

TreeSet ts = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                D d1 = (D)o1;
                D d2 = (D)o2;
                return d1.age > d2.age ? -1
                        : d1.age < d2.age ? 1 : 0;
            }
        });
        ts.add(new D(5));
        ts.add(new D(-3));
        ts.add(new D(9));
        ts.add(new D(11));
        ts.add(new D(2));
        System.out.println(ts);
/** 运行结果:
[D[age:11], D[age:9], D[age:5], D[age:2], D[age:-3]]
*/

自然排序、定制排序、Comparator决定的是谁大的问题,即按什么顺序(升序、降序)进行排序
它们的关注点是不同的,一定要注意区分


EnumSet

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值