java集合之HashSet学习(基本概念及用法,部分源码分析,TreeSet扩展)

一.HashSet基础学习

1.何为HashSet?

顾名思义,它是哈希集。很明显,它是利用hash原理来存储数据的,hash最大的特点就是无序和无重复性,但查询快。

2.我们为何需要它?(应用场景)

链表和数组可以按照我们的意思排列元素的次序,但是如果我们不知道我们要查找元素的位置,我们就需要遍历数组和链表直到找到为止,可想而知,如果元素很多,那将花费很多的时间。但如果不在意元素的顺序,那么HashSet在常用于按照值进行是否存在检验的情况下是个很好的选择,因为HashSet中的元素一定是不重复的

3.构造方法

构造方法描述
HashSet()构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。
HashSet(Collection<? extends E> c)构造一个包含指定集合中的元素的新集合。
HashSet(int initialCapacity)构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负因子。

负载因子决定何时对哈希表再哈希,例如负载因子为0.75,当表中超过75%的位置已经填入元素,这个表进行双倍扩增。

4.其他方法学习

方法描述
boolean add(E e)将指定的元素添加到此集合(如果尚未存在)。
void clear()从此集合中删除所有元素。
Object clone()返回此 HashSet实例的浅层副本:元素本身不被克隆。
boolean contains(Object o)如果此集合包含指定的元素,则返回 true 。
boolean isEmpty()如果此集合不包含元素,则返回 true 。
Iterator iterator()返回此集合中元素的迭代器。
boolean remove(Object o)如果存在,则从该集合中删除指定的元素。
int size()返回此集合中的元素数(其基数)。
Spliterator spliterator()在此集合中的元素上创建late-binding和故障快速 Spliterator 。
使用例子
package JavaHeXinJiShu;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class example9_2 {
    public static void main(String[] args) throws IOException {

        Set<String> words = new HashSet<>(); // 初始容量为16,负载因子为0.75
        long totalTime = 0;

        try (Scanner in = new Scanner("we should working hard"))
        {
            while (in.hasNext())//hashNext()默认以空格为分界符
            {
                String word = in.next();
                words.add(word);//向哈希表中添加元素
               
            }
        }

        Iterator<String> iter = words.iterator();//得到words的遍历器
        for (int i = 1; i <= 20 && iter.hasNext(); i++)
            System.out.println(iter.next());
        System.out.println(". . .");
    }
}

在这里插入图片描述
可以明显看到添加后的遍历顺序是无序的。

二.HashSet是如何被实现的?

1.查看源码我们可以看到

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

它主要继承了AbstracSet类和实现了Set接口。

2.查看Set接口定义的方法

boolean	add(E e)
如果指定的元素不存在,则将其指定的元素添加(可选操作)。

boolean	addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合(如果尚未存在)(可选操作)。

voidclear()
从此集合中删除所有元素(可选操作)。

boolean	contains(Object o)
如果此集合包含指定的元素,则返回 trueboolean	containsAll(Collection<?> c)
返回 true如果此集合包含所有指定集合的元素。

boolean	equals(Object o)
将指定的对象与此集合进行比较以实现相等。

int	hashCode()
返回此集合的哈希码值。

boolean	isEmpty()
如果此集合不包含元素,则返回 true 。

Iterator<E>	iterator()
返回此集合中元素的迭代器。

boolean	remove(Object o)
如果存在,则从该集合中删除指定的元素(可选操作)。

boolean	removeAll(Collection<?> c)
从此集合中删除指定集合中包含的所有元素(可选操作)。

boolean	retainAll(Collection<?> c)
仅保留该集合中包含在指定集合中的元素(可选操作)。

int	size()
返回此集合中的元素数(其基数)。

default Spliterator<E>	spliterator()
在此集合中的元素上创建一个 Spliterator 。

Object[] toArray()
返回一个包含此集合中所有元素的数组。

<T> T[]	toArray(T[] a)
返回一个包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。

3.我们知道AbstracSet类主要实现了Set接口

我们下面看看它重点实现了那些方法:

boolean	equals(Object o)//将指定的对象与此集合进行比较以实现相等。

int	hashCode()//返回此集合的哈希码值。

boolean	removeAll(Collection<?> c)//从此集合中删除指定集合中包含的所有元素(可选操作)。

源码也不长:

package java.util;
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {
   
    protected AbstractSet() {
    }

    //将指定的对象与此集合进行比较以实现相等。
    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return 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;
    }

   //从此集合中删除指定集合中包含的所有元素(可选操作)。
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(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;
    }

}

4.OK我们了解了HashSet的依赖类,下面我们来分析HashSet类

1)我们首先学习一个HashSet是如何被建立的:
 Set<String> words = new HashSet<>();

通过上面简单的语句我们创建了一个名为words的HashSet的对象,
实际上它背后是这样实现的:
我们知道创建一个实例调用的是构造方法,以上面的空参数构造方法为例,查询源码我们有:

 private transient HashMap<E,Object> map;
public HashSet() {
        map = new HashMap<>();
    }

从上面来看HashSet里面用到了HashMap,下面我们进一步探索:

 static final float DEFAULT_LOAD_FACTOR = 0.75f;
 public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

从上面我们可以看出这里map的负载因子为0.75,也就是设置哈希表的负载因子。

2)然后我们来看add方法:
 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    
  public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

从上面看到,向HashSet中添加元素实际上是利用HashMap来存储元素的,故它是线程不安全的

到此我们可以看出HashSet的基本实现元素了,至于更深入的方法探索,我们可以上面的基础上来探索。

二.树集

我们都知道Set用于存储无重复元素的集合,前面的HashSet用于存储的虽然是不重复的集合,但它存储过后遍历出来的是一组无序的集合,如果我们即想要一个用于存储无重复的集合又要能对它进行顺序遍历,我们可以采用TreeSet这一数据结构。

1.什么是树集(TreeSet)?

树集是一个有序集合,它可以以任意顺序将元素插入到集合中,在对集合进行遍历的时候每个值将自动的按照排序后的顺序呈现。

2.它是如何实现的?

现在它是通过红黑树存储元素来实现排序的,因为红黑树良好的搜索特性(限高),所以查找元素的时间复杂度只是略小于HashSet,如果要实现对集合的排序,我们可以选择TreeSet进行存储。
但如果不需要的话,还是建议选择HashSet

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小牧之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值