JDk Set及HashSet源码解析

Set源码解析

首先我们看看在JDK中Set端口的源码:

package asdf;

import java.util.*;
import java.util.Collection;

/**
 * Created by ping.miao on 2015/7/29.
 * set源码分析
 */
public interface Set<E> extends Collection<E> {
    int size();

    /**
     * @return Set包含元素的个数
     */
    boolean isEmpty();
    /**
     * @return 判断Set是否为空,为空返回true,不为空返回false
     */
    boolean contains(Object o);
    /**
     * @return 判断set是否包含元素与o相等,假如o == null,那么只需要判断set中是否有元素为null
     * 假如有返回true,没有返回false,假如 o != null,判断set中是否有元素与o相等,有返回true,
     * 没有返回false
     */
    Iterator<E> iterator();
    /**
     * 返回包含set所有元素的Iterator
     */
    Object[] toArray();
    /**
     * 返回包含set所有元素的array
     */
    <T> T[] toArray(T[] a);
    /**
     * 返回一个包含set元素的指定类型的数组
     */
    boolean add(E e);
    /**
     * 插入元素,假如当前set中存在元素与e相等,那么保持原set不改变,返回false,
     * 否则插入元素,并返回true
     */
    boolean remove(Object o);
    /**
     * remove类似于这样的元素(o == null? e == null : o.equals(e)),并返回true
     */
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean retainAll(Collection<?> c);
    boolean removeAll(Collection<?> c);
    void clear();
    boolean equals(Object o);
    int hashCode();

}
从源码可以看出Set端口是Collection端口的子端口,上面的代码中已经做了详细的注解,这里不再赘述Set中各个方法是用来干什么的。

接下来我们看看HashSet的源码:

HashSet源码:

首先看看HashSet到底是一个什么样的类,它继承了哪些类。

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

由上面的HashSet的头部可以看到,HashSet是AbstractSet的子类、Set的子接口,并且实现了其他额外的一些东西,Cloneable和Serializable。我们先看看AbstractSe中的部分源码。

package asdf;

import java.util.*;
import java.util.Collection;
import java.util.Set;

/**
 * Created by ping.miao on 2015/7/30.
 */
public abstract class AbstractSet<E> extends AbstractCollection<E> implements java.util.Set<E> {
    protected AbstractSet() {
    }
    /**
     * 构造方法
     */

    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        java.util.Collection c = (Collection) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }
    /**
     * 比较Object o与set是否相等,o可以是单个元素,也可以是一个Set。o是单个元素的时候
     * 假如set中存在与o相等的元素返回true,否则返回false。o是Set的时候,假若它与当前
     * set长度相同,并且每个元素相等,返回true,否则返回false。
     */
    
    public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }
    /**
     * 得到set的hashcode。假如当前值为null的时候,它的hashcode是0,最后返回的是set中
     * 所有元素的hashcode的总和。
     */

    public boolean removeAll(Collection<?> c) {
        boolean modified = false;

        if (size() > c.size()) {
            for (Iterator<?> i = c.iterator(); i.hasNext(); )
                modified |= remove(i.next());
        } else {
            for (Iterator<?> i = iterator(); i.hasNext(); ) {
                if (c.contains(i.next())) {
                    i.remove();
                    modified = true;
                }
            }
        }
        return modified;
    }
    /**
     * 删除参数中给出的set中的所有元素,删除成功返回true,失败返回false
     */
    
}
底层实现:

/**
     * 底层使用HashMap来保存HashSet中的所有元素
     */
    private transient HashMap<E,Object> map;
    /**
     * 定义一个虚拟的Object对象作为HashMap的value
     */
    private static final Object PRESENT = new Object();

构造方法:

/**
     * 无参数构造方法,构造一个空的HashSet
     * 实际的底层会初始化一个空的HashMap,并且使用默认容量16,填装因子0.75
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * 构造一个包含指定的Collection中的元素的set
     * 实际底层使用默认的填装因子0.75和可以包含Collection中所有元素的初始容量来
     * 创建一个新的HashMap
     *
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
     * 以指定的initialCapacity和loadFactor创建一个空的HashSet
     * @param initialCapacity HashSet大小初始值
     * @param loadFactor 填装因子
     *
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     *
     * @param initialCapacity HashSet大小初始值,构造一个空的HashSet
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     *
     * @param initialCapacity HashSet初始大小
     * @param loadFactor 填装因子
     * @param dummy 标记
     * 返回一个空的LinkedHashSet
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
数据插入删除:

/**
     *
     * @param e 待插入元素,若没有满足(o == null ? e == null : o.equals(e))
     *          的元素则插入
     * @return 插入成功返回true,插入失败返回false
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    /**
     *
     * @param o 若o存在则删除
     * @return true or false,删除成功返回true,删除失败返回false
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }<strong>
</strong>
数据读取和遍历:

/**
     *
     * @return 返回Set的所有元素,并且这些元素是没有特定顺序的
     * 底层调用HashMap的keySet返回所有的key
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
看到这里大家估计很困惑,set里面的元素是不能重复的,而且值为null的元素只能有一个,这些是怎么来实现的呢。

首先我们看看HashSet的构造方法:


你会发现HashSet调用了一个方法addAll,接下来我们去addAll方法看看它是怎么实现的。



addAll是AbstractCollection的方法,看到上面的源码,我们似乎应该去看看add方法是怎么实现的,点到add方法,发现它只是抛出一个异常,看到这里,你可能会说,这个没有实现啊,对,这个方法确实没有展现给我们它到底是怎么实现的。但是它总是用某个方法实现了,我们去看看HashSet的add方法是怎么来实现的:



HashSet调用了HashMap的put方法,接下来我们去看看HashMap的put方法的源码是怎么实现的:



可以看出是通过对key产生的hashcode和判断是否set中已有值与它相等来判断set中是否有重复的值的。接下来我们看看hashcode是怎么产生的:









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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值