Set集合接口----HashSet和TreeSet

Set接口与List接口最大的不同在于Set接口中的内容是不允许重复的,同时,Set接口并没有对Collection接口进场扩充,而List接口对Collection进行了扩充,因此,Set接口中也没有get()方法

Set接口包括:HashSet(无序存储)TreeSet(有序存储)

来看一下HashSet的源码:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;    //E就是Set

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

 可以发现Set的内部其实是用Map实现的,保存的元素E存储在HashMap中的Value上

(1)HashSet 

特点:

 不能保证元素的排列顺序,顺序有可能发生变化
 不是同步的
 集合元素可以是null,但只能放入一个null

添加元素

public class TestHashSet {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();  //<>泛型,可以使任何数据类型
        Set<Integer> set1 = new HashSet<>(); //但是要包装类,这里不能是int型
        set.add("A");
        set.add("B");
        set.add("C"); //重复元素,被后一个覆盖,不在结果中
        set.add("C"); 
        set1.add(1);
        set1.add(2);
        set1.add(2); //重复元素
        set1.add(3);
        set1.add(4);
        System.out.println(set);
        System.out.println(set1);
    }
}

结果:

[A, B, C]
[1, 2, 3, 4]

Process finished with exit code 0

 看到这里你可能要迷惑了,HashSet不是无序存储嘛,怎么输出结果是有序的???emmm......听我慢慢说

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置,这里其实只是Hash Code的算法上的巧合;在小范围内给人以有序的假象;

[A, B, C]
[8080, 1024, 1, 2, 3, 6, 10]

Process finished with exit code 0

当我插入几个大的数字的时候,输出结果就变成无序的了

总结:

(1)当输入的值大小和底层数组大小差不多的时候,HashSet的输入顺序是无序的,但输出是有序的,因为这些数的哈希值,在底层是顺序拍的,故输出也是有序的

(2)而当输入的值大小和底层数组相差很大时,大的数的哈希值有可能是小于比他小的数的,这时候输出的结果就是无序的。

(2)TreeSet

TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

元素要想保存到TreeSet中,要么元素本身所在的类实现Comparable,要么通过外部传入一个比较器(外部排序)

 

自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。

添加元素

public class TestTreeSet {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        Set<Integer> set1 = new TreeSet<>();
        set.add("C");
        set.add("C");  //重复元素
        set.add("A");
        set.add("B");
        set1.add(8080);
        set1.add(6);
        set1.add(1024);
        set1.add(3);
        set1.add(10);
        set1.add(1);
        set1.add(2);
        set1.add(2);  //重复元素
        System.out.println(set);
        System.out.println(set1);
    }
}

结果:

[A, B, C]
[1, 2, 3, 6, 10, 1024, 8080]

Process finished with exit code 0

由TreeSet的输出结果发现上面被HashSet输出的无序结果变为有序了,TreeSet会进行升序排列输出 

(3)重复元素判断 

在使用TreeSet子类进行数据保存的时候,重复元素的判断依靠的ComParable接口完成的(或者从外部传入一个比较器)。相等返回0,只要返回的是0,就代表元素重复

但是HashSet跟Comparable没有任何关系,所以它判断重复元素的方式依靠的是Object类中的两个方法:

    1. hash码: public native int hashCode();
    2. 对象比较:public boolean equals(Object obj);

hashCode与equals的关系:

  • hashCode:取得任意一个对象的哈希码
  • equals:比较两个对象是否相等

hashCode返回值相等的对象,equals是否相等?不一定相等 (x 与 f(x)的关系)

equals返回值相等的两个对象,两个对象的hashCode是否相等? 一定相等

也就是说如果要想标识出对象的唯一性,一定需要equals()与hashCode()方法共同调用。HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等

注意: 如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。 

(4)HashSet和TreeSet的区别与联系

联系:

  • 都是Set的子接口
  •  单例集合
  • 都要求数据不可重复

区别:

  • HashSet是哈希表实现的,数据时候无序的,允许放入null值,但只能放入一个null
  • TreeSet是二叉树实现的,数据是自动排好序的,且是升序,不允许放入null值
  • Hashset是通过复写hashCode()方法和equals()方法来保证存储数据方式的唯一
  • Treeset是通过Compareable接口的compareto方法来保证存储数据方式的唯一
  • HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的 String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值