基础储备----Java集合(二): Set

作为一名Java开发人员, 集合类是日常操作中经常遇到. 尽管我们要避免重复造轮子, 但是, 我们只有对自己用的轮子有充分认识才能用好它. 这个系列文章主要是记录集合类相关的知识点.
摘要由CSDN通过智能技术生成


1. 前言

作为一名Java开发人员, 集合类是日常操作中经常遇到. 尽管我们要避免重复造轮子, 但是, 我们只有对自己用的轮子有充分认识才能用好它. 这个系列文章主要是记录集合类相关的知识点.
特别声明: 大部分内容并非原创, 引用自参考链接.
本文承接上一篇文章基础储备—-Java集合(一): List

2. Set

Set继承于Collection接口, 是一个不允许出现重复元素, 并且无序的集合, 主要有HashSet和TreeSet两大实现类.
在判断重复元素的时候, Set集合会调用hashCode()和equal()方法来实现.
HashSet是哈希表结构, 主要利用HashMap的key来存储元素, 计算插入元素的hashCode来获取元素在集合中的位置;
TreeSet是红黑树结构, 每一个元素都是树中的一个节点, 插入的元素都会进行排序;

2.1 Set常用操作

与List接口一样, Set接口也提供了集合操作的基本方法.
但与List不同的是, Set还提供了equals(Object o)和hashCode(), 供其子类重写, 以实现对集合中插入重复元素的处理;

public interface Set<E> extends Collection<E> {
   

    A:添加功能
    boolean add(E e);
    boolean addAll(Collection<? extends E> c);

    B:删除功能
    boolean remove(Object o);
    boolean removeAll(Collection<?> c);
    void clear();

    C:长度功能
    int size();

    D:判断功能
    boolean isEmpty();
    boolean contains(Object o);
    boolean containsAll(Collection<?> c);
    boolean retainAll(Collection<?> c); 

    E:获取Set集合的迭代器:
    Iterator<E> iterator();

    F:把集合转换成数组
    Object[] toArray();
    <T> T[] toArray(T[] a);

    //判断元素是否重复,为子类提高重写方法
    boolean equals(Object o);
    int hashCode();
}

2.2 初识HashSet

HashSet实现Set接口, 底层由HashMap(后面讲解)来实现, 为哈希表结构, 新增元素相当于HashMap的key, value默认为一个固定的Object. HashSet相当于一个阉割版的HashMap;
当有元素插入的时候, 会计算元素的hashCode值, 将元素插入到哈希表对应的位置中来;
它继承于AbstractSet, 实现了Set, Cloneable, Serializable接口.
(1)HashSet继承AbstractSet类, 获得了Set接口大部分的实现, 减少了实现此接口所需的工作, 实际上是又继承了AbstractCollection类;
(2)HashSet实现了Set接口, 获取Set接口的方法, 可以自定义具体实现, 也可以继承AbstractSet类中的实现;
(3)HashSet实现Cloneable, 得到了clone()方法, 可以实现克隆功能;
(4)HashSet实现Serializable, 表示可以被序列化, 通过序列化去传输, 典型的应用就是hessian协议.
具有如下特点:
(1)不允许出现重复因素;
(2)允许插入Null值;
(3)元素无序(添加顺序和遍历顺序不一致);
(4)线程不安全, 若2个线程同时操作HashSet, 必须通过代码实现同步;

2.2.1 HashSet元素添加

Set集合不允许添加重复元素, 那么到底是个怎么情况呢?
来看一个简单的例子:

public class HashSetTest {
    public static void main(String[] agrs){
        //hashCode() 和 equals()测试:
        hashCodeAndEquals();
    }
    public static void hashCodeAndEquals(){
        //第一个 Set集合:
        Set<String> set1 = new HashSet<String>();
        String str1 = new String("jiaboyan");
        String str2 = new String("jiaboyan");
        set1.add(str1);
        set1.add(str2);
        System.out.println("长度:"+set1.size()+",内容为:"+set1);
        //第二个 Set集合:
        Set<App> set2 = new HashSet<App>();
        App app1 = new App();
        app1.setName("jiaboyan");
        App app2 = new App();
        app2.setName("jiaboyan");
        set2.add(app1);
        set2.add(app2);
        System.out.println("长度:"+set2.size()+",内容为:"+set2);
        //第三个 Set集合:
        Set<App> set3 = new HashSet<App>();
        App app3 = new App();
        app3.setName("jiaboyan");
        set3.add(app3);
        set3.add(app3);
        System.out.println("长度:"+set3.size()+",内容为:"+set3);
    }
}

测试结果:

长度:1,内容为:[jiaboyan]
长度:2,内容为:[App@74a14482, App@4554617c]
长度:1,内容为:[App@1540e19d]

可以看到, 第一个Set集合中最终只有一个元素; 第二个Set集合保留了2个元素; 第三个集合也只有1个元素;
究竟是什么原因呢?
让我们来看看HashSet的add(E e)方法:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

在底层HashSet调用了HashMap的put(K key, V value)方法:

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

简单概括如下:
在向HashMap中添加元素时, 先判断key的hashCode值是否相同, 如果相同, 则调用==, equals()进行判断, 若相同则覆盖原有元素; 如果不同, 则直接向Map中添加元素;
反过来, 我们在看下上面的例子:
在第一个Set集合中, 我们new了两个String对象, 赋了相同的值. 当传入到HashMap中时, key均为”jiaboyan”, 所以hash和i的值都相同. 进行if (e.hash == hash &

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值