java之Set详解

~ 前言

讲完前面的HashMap和LinkedHashMap之后接下来就是Set了,这里只讲两个(HashSet与LinkedHashSet)。
后续讲解内容为源码实现,这里使用的是JDK8的版本。


HashSet

HashSet类,是存在于java.util包中的类 。同时也被称为集合,该容器中只能存储不重复的对象。
对于 HashSet 而言,它是基于 HashMap 实现的,底层采用 HashMap 来保存元素,所以如果对 HashMap与LinkedHashMap 比较熟悉了,那么学习 HashSet 也是很轻松的。
先来认识一下很重要的两个成员。

	// 实际存储数据结构
	private transient HashMap<E,Object> map;

	// 存入HashMap的value
    private static final Object PRESENT = new Object();

然后我们来看一下它的关系图。

请添加图片描述

通过关系图我们能清楚的看到它的实现:

  1. iterable,可以使用迭代器
  2. collection,有add、remove等方法
  3. cloneable,可以克隆
  4. serializable,可以被序列化

初始化

简单分析一下:

	// 默认初始化存储结构
	public HashSet() {
        map = new HashMap<>();
    }

	// 根据传入集合存储到HashMap中
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    
    // 根据传入值自定义HashMap
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

	// 根据传入值自定义HashMap
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

	// 基于LinkedHashMap为存储数据实现
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

这样就看完了初始化的代码,是不是很简单呢?


~ 基本方法解析

add

在传入集合的初始化方法中有调用到一个方法 addAll(),而底层就是调用了add()方法。

// 调用add()方法
public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

// 调用HashMap或LinkedHashMap的put()方法
// PRESENT = new Object();
public boolean add(E e) {
	return map.put(e, PRESENT)==null;
}

非常简单。


remove
public boolean remove(Object o) {
 return map.remove(o)==PRESENT;
}

也是调用了Map的方法,到这里我们发现基本上所有方法都是对Map存储结构进行封装,那么剩下的就交给同学们继续看了。

请添加图片描述


LinkedHashSet

LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序,保持元素的添加顺序。LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。我们先看一下它的关系图。

请添加图片描述

我们可以发现它继承至HashSet,也就说它基于HashSet来实现的,那么为什么说插入时性能稍微逊色于HashSet?移除与获取节点也会逊色于HashSet?

因为我们可以看到LinkedHashSet都是以LinkedHashMap为数据结构来实现的,而HashSet可以进行选择HashMap或LinkedHashMap。所以我们实际上是对于底层的数据结构进行分析,就像数据库一样,存储与查询的性能都是依赖于底层的数据结构实现与优化。那我们对比一下LinkedHashMap与HashMap两个实现会发现,LinkedHashMap在调用基本API方法时都会有回调方法来维护本身的链表,这里就造成了多于HashMap的性能开销,所以这里得出的结论就是性能可能稍微逊色与HashSet。


~ 初始化

	public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }
	
    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }

上面的4个初始化方法都是直接调用了HashSet的初始化方法

	// 基于LinkedHashMap为存储数据实现
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
}

然后我们查看源码可以发现所有的基础api都是直接调用了HashSet的方法,所以到这里我们就把LinkedHashSet看完了。


最后

这一章可以说讲解了个寂寞,十分简单。
如果有些同学不了解实现原理的话,就需要先看一下前面的文章(HashMap、LinkedHashMap)。
请添加图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值