java HashSet底层实现

Thinking in JavaSet接口、HashSet源码学习

 

  文章主要讨论Set接口的设计、以及Set接口的一个实现类HashSet的设计细节。对于他们的思考,同样是基于源码学习的。

Set接口设计

通过阅读API和源码我们可以知道Java中的Set和数学行直观的“集”的概念是相同的。Set的最大特点也就是不允许在其中放入重复的元素。Set集合最多只能包含一个null元素。至于这种特点是如何实现的,我们先不考究。在其具体子类HashSet里我们在讨论之。广州java培训咨询QQ:707552864,544627560

Set接口源码解析

  首先看看Set的源码吧。

[java] view plaincopy

package com.kiritor;

/**

Set源码研究*/

import java.util.Iterator;

public interface Set extends Collection {

int size();

boolean isEmpty();

boolean contains(Object o);

Iterator iterator();

Object[] toArray();

T[] toArray(T[] a);

boolean add(E e);

boolean remove(Object o);

boolean containsAll(Collection c);

boolean addAll(Collection c);

boolean retainAll(Collection c);

boolean removeAll(Collection c);

void clear();

boolean equals(Object o);

int hashCode();

}

  可以看出的是Set继承至Collection,而且通过对比还可以知道的是,Set提供的方法和Colllection指定的方法是完全一样的。那么它的不重复是如何体现的呢?这里我们通过研究其具体实现类HashSet来说明。

HashSet类实现

  同样的对于HashSet类的具体源码笔者就不贴出来了。这里我们只是简要的对其的方法做一些分析。首先看看HashSet类的头部吧。

  对于序列化、Cloneable接口笔者就不细说了。这里HashSet继承AbstactSet这个中间抽象类,并且这个抽象类又继承至AbstractCollection。这里简要的说说自己的理解。在前一章对ArrayList的学习中,笔者并未就这方面给予解释。AbstractCollection其实更像是实现ListSet的共同的方法,而AbstactSet AbstactList更像是提供给SetList各自特有方法的实现。

1、底层实现

 

  通过其源码的观察可以知道的是HashSet的底层实现是基于HashMap的。它不保证Set的迭代顺序而且不保证该顺序永久不变。HashSet的实现较为的简单,其相关的操作都是通过直接调用底层HashMap的相关方法来完成。

[java] view plaincopy

// 底层使用HashMap来保存HashSet中所有元素。

private transient HashMap map;

// 定义一个虚拟的Object对象作为HashMapvalue,将此对象定义为static final

private static final Object PRESENT = newObject();

2、构造方法

[java] view plaincopy

* 默认的无参构造器,构造一个空的HashSet

* 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75

*/

public HashSet() {

map = new HashMap();

}

/**

* 构造一个包含指定collection中的元素的新set

*

* 实际底层使用默认的加载因子0.75和足以包含指定

* collection中所有元素的初始容量来创建一个HashMap

* @param c 其中的元素将存放在此set中的collection

*/

public HashSet(Collection c) {

map = new HashMap(Math.max((int)(c.size()/.75f) + 1, 16));

addAll(c);

}

/**

* 以指定的initialCapacityloadFactor构造一个空的HashSet

*

* 实际底层以相应的参数构造一个空的HashMap

* @param initialCapacity 初始容量。

* @param loadFactor 加载因子。

*/

public HashSet(int initialCapacity, floatloadFactor) {

map = new HashMap(initialCapacity,loadFactor);

}

/**

* 以指定的initialCapacity构造一个空的HashSet

*

* 实际底层以相应的参数及加载因子loadFactor0.75构造一个空的HashMap

* @param initialCapacity 初始容量。

*/

public HashSet(int initialCapacity) {

map = new HashMap(initialCapacity);

}

/**

* 以指定的initialCapacityloadFactor构造一个新的空链接哈希集合。

* 此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。

*

* 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。

* @param initialCapacity 初始容量。

* @param loadFactor 加载因子。

* @param dummy 标记。

*/

HashSet(int initialCapacity, floatloadFactor, boolean dummy) {

map = new LinkedHashMap(initialCapacity, loadFactor);

}

3、存储元素

[java] view plaincopy

/**

* 如果此set中尚未包含指定元素,则添加指定元素。

* 更确切地讲,如果此set 没有包含满足(e==null ? e2==null : e.equals(e2))

* 的元素e2,则向此set 添加指定的元素e

* 如果此set已包含该元素,则该调用不更改set并返回false

*

* 底层实际将将该元素作为key放入HashMap

* 由于HashMapput()方法添加key-value对时,当新放入HashMapEntrykey

* 与集合中原有Entrykey相同(hashCode()返回值相等,通过equals比较也返回true)

* 新添加的Entryvalue会将覆盖原来Entryvalue,但key不会有任何改变,

* 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中,

* 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。

* @param e 将添加到此set中的元素。

* @return 如果此set尚未包含指定元素,则返回true

*/

public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

4、读取、遍历元素

[java] view plaincopy

/**

* 返回对此set中元素进行迭代的迭代器。返回元素的顺序并不是特定的。

*

* 底层实际调用底层HashMapkeySet来返回所有的key

* 可见HashSet中的元素,只是存放在了底层HashMapkey上,

* value使用一个staticfinalObject对象标识。

* @return 对此set中元素进行迭代的Iterator

*/

public Iterator iterator() {

urn map.keySet().iterator();

}

5、删除元素

[java] view plaincopy

/**

* 如果指定元素存在于此set中,则将其移除。

* 更确切地讲,如果此set包含一个满足(o==null ? e==null : o.equals(e))的元素e

* 则将其移除。如果此set已包含该元素,则返回true

* (或者:如果此set因调用而发生更改,则返回true)(一旦调用返回,则此set不再包含该元素)

*

* 底层实际调用HashMapremove方法删除指定Entry

* @param o 如果存在于此set中则需要将其移除的对象。

* @return 如果set包含指定元素,则返回true

*/

public boolean remove(Object o) {

return map.remove(o)==PRESENT;

}

 

 


你可能感兴趣的:(java,hashset)