Java集合类学习(三) Set集合

5 篇文章 0 订阅
5 篇文章 0 订阅

1 Set集合类型

1.1 Set

  • 特点:无序、唯一
  • Set集合与数学上定义的集合的特点一致
    > 在这里插入图片描述

1.2 HashSet

  • 采用Hashtable哈希表存储结构
  • 优点:添加速度快 查询速度快 删除速度快
  • 缺点:无序
  • 注意:HashSet存储自定义对象时,为保证其“唯一性”,应重写实体类中的equals和hashcode方法。LinkedHashSet 也一样。
    在这里插入图片描述

1.3 LinkedHashSet

  • 采用哈希表存储结构,同时使用链表维护次序
  • 有序(添加顺序)
    这里是引用

1.4 TreeSet

  • 采用二叉树(红黑树)的存储结构
  • 优点:有序(自然顺序)、 查询速度比List快(按照内容查询)
  • 缺点:查询速度没有HashSet快
  • 注意:TreeSet存储自定义对象时为了保证其有序性,应在实体类中实现Comparable接口。
    在这里插入图片描述

2 Hash表原理

2.1 Hash表的结构和特点

hashtable 也叫散列表;
结构:结构有多种。最常见的是:顺序表+链表
主结构:顺序表,每个顺序表的节点在单独引出一个链表
在这里插入图片描述

2.2 Hash表如何添加数据

  1. 计算哈希 码(调用hashCode(),结果是一个int值,整数的哈希码取自身即可)
  2. 计算在哈希表中的存储位置 y=k(x)=x%11
    x:哈希码 k(x) 函数y:在哈希表中的存储位置
  3. 存入哈希表
  • 情况1:一次添加成功
  • 情况2:多次添加成功(出现了冲突,调用equals()和对应链表的元素进行比较,比较到最后,结果都是false,创建新节点,存储数据,并加入链表末尾)
  • 情况3:不添加(出现了冲突,调用equals()和对应链表的元素进行比较, 经过一次或者多次比较后,结果是true,表明重复,不添加)

2.3 Hash表如何查找数据

与添加的步骤类似

2.4 Hash码如何计算

  • int :取自身 看Integer的源码
  • double :看源码
 public static int hashCode(double value) {
        long bits = doubleToLongBits(value);
        return (int)(bits ^ (bits >>> 32));//右移32位后按位与运算
 }
  • String:看源码
public int hashCode() {
     int h = hash;
     if (h == 0 && value.length > 0) {
         char val[] = value;
         for (int i = 0; i < value.length; i++) {
             h = 31 * h + val[i];
         }
         hash = h;
     }
     return h;
 }
  • 实体类:先计算各个属性的哈希码,再进行某些相加相乘的运算

2.5 如何减少哈希冲突

2.5.1 哈希表的长度和表中的记录数的比例–装填因子:

如果Hash表的空间远远大于最后实际存储的记录个数,则造成了很大的空间浪费, 如果选取小了的话,则容易造成冲突。 在实际情况中,一般需要根据最终记录存储个数和关键字的分布特点来确定Hash表的大小。还有一种情况时可能事先不知道最终需要存储的记录个数,则需要动态维护Hash表的容量,此时可能需要重新计算Hash地址。
装填因子=表中的记录数/哈希表的长度
如果装填因子越小,表明表中还有很多的空单元,则添加发生冲突的可能性越小;而装填因子越大,则发生冲突的可能性就越大,在查找时所耗费的时间就越多。 有相关文献证明当装填因子在0.5左右时候,Hash性能能够达到最优。
因此,一般情况下,装填因子取经验值0.5。

2.5.2 哈希函数的选择

  • 直接定址法
  • 平方取中法
  • 折叠法
  • 除留取余法(y = x%11)

2.5.3 处理冲突的方法

  • 链地址法
  • 开放地址法
  • 再散列法
  • 建立一个公共溢出区

3 HashSet源码分析

  • HashSet的底层使用的是HashMap,所以底层结构也是哈希表
  • HashSet的元素到HashMap中做key,value统一是指向同一个Object(),最终源码还是要回到Map中去看
public class HashSet<E> implements Set<E> {
    private transient HashMap<E, Object> map;//底层是HashMap
    private static final Object PRESENT = new Object();//Map的Value
    public HashSet() {
        map = new HashMap<>();
    }
    public boolean add(E e) {
         return map.put(e, PRESENT)==null;
    }
    public int size() {
        return map.size();
    }
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
}

4 TreeSet源码分析

  • TreeSet的底层使用的是TreeMap,所以底层结构也是红黑树
  • TreeSet的元素e是作为TreeMap的key存在的,value统一为同一个 Object(),最终源码还是要回到Map中去看
public class TreeSet<E> implements NavigableSet<E> {
    //底层的TreeMap的引用
    private transient NavigableMap<E, Object> m; 
    private static final Object PRESENT = new Object();
    public TreeSet() {
    //创建TreeSet对象就是创建一个TreeMap对象
        this(new TreeMap<E, Object>()); 
    }
    TreeSet(NavigableMap<E, Object> m) {
        this.m = m;
    }
    public boolean add(E e) {
        return m.put(e, PRESENT) == null;
    }
    public int size() {
        return m.size();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值