Java集合 | HashSet(JDK 1.7)

一、基本图示



二、基本介绍
结构
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
  • HashSet 继承了 AbstractSet 抽象类,AbstractSet 接口继承了 AbstractCollection 抽象类
  • HashSet 实现了 Set 接口
  • HashSet 实现了 Coneable 类,覆盖了 clone 方法,可以被克隆
  • HashSet 实现了 Serializable 接口,即能被序列化和反序列化
特性
  • HashSet 底层依然是由 HashMap 实现的
  • 元素的顺序是无序
  • HashSet 的操作是线程不安全
  • HashSet 中可以存放 null 的(因为 HashMap 允许存放 key 为 null 的键值对),不允许存放重复的值,包括重复的 null 值

三、基本参数

HashSet 的底层是由 HashMap 实现的,HashSet 将值存放在 HashMap 的 key 中,value 则是同一个对象

private transient HashMap<E,Object> map;

这里的 PRESENT 其实是同一个对象,存放在 HashMap 的 value 中,所有的 key 都对应这同一个 value,即 PRESENT 对象

private static final Object PRESENT = new Object();

四、初始化方法

无参构造函数,实例化一个空的HashMap,初始容量为 16,加载因子为 0.75

public HashSet() {
    map = new HashMap<>();
}

如果传入初始容量,那么底层的 HashMap 的初始容量就是传入的值,加载因子还是 0.75

public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

如果既传入初始容量,又传入负载因子,则底层的 HashMap 使用的就是传入的初始容量和负载因子

public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

传入一个 Collection 集合对象

public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

五、添加元素

在 HashSet 中,添加元素在底层实际是对 HashMap 进行操作,即调用了 HashMap 的 put 方法。key 是添加的元素,value 则是同一个 PRESENT 对象

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

HashSet 的 add 方法里面调用 HashMap 的 put 方法,对于放入的值有如下的规定:

  1. 传入的 null 永远只会放在数组的第 0 位,且只能放1个 null,因为HashMap 中的 value 永远是同一个对象,因此后面传入的 null 会将前面传入的 null 覆盖
  2. 先根据传入值的 hash 值计算在数组中的位置,然后遍历数组中在这个位置上的单链表,如果有和该值的 hash 值和 key 值一样,或者 key 的地址一样的,则在单链表中使用新值将旧的值覆盖
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;
}

六、删除元素

HashSet 的 remove 方法,底层调用的是 HashMap 的 remove 方法

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

该方法根据 key 删除 Entry 键值对

public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
}

七、遍历HashSet

在对 HashSet 进行遍历的时候,分两步来执行:

  1. 调用 HashMap 的 keySet() 方法获取键的集合
  2. 调用 HashMap 的内部类 KeySet 的 iterator 方法对键的集合进行遍历
public Iterator<E> iterator() {
    return map.keySet().iterator();
}

public Set<K> keySet() {
    Set<K> ks = keySet;
    return (ks != null ? ks : (keySet = new KeySet()));
}

private final class KeySet extends AbstractSet<K> {
    public Iterator<K> iterator() {
        return newKeyIterator();
    }
   ...
}

八、其他方法
获取集合长度
public int size() {
    return map.size();
}
判断集合是否为空
public boolean isEmpty() {
    return map.isEmpty();
}
判断集合是否包含某一对象
public boolean contains(Object o) {
    return map.containsKey(o);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值