Java中的HashSet、LinkedHashSet集合解析

 更新时间:2023年11月01日 09:31:01   作者:追光而遇  
这篇文章主要介绍了Java中的HashSet、LinkedHashSet集合解析,与HashSet不同的是,LinkedHashSet在内部使用了一个双向链表来维护元素的顺序,因此它可以保持元素的插入顺序,这使得LinkedHashSet在需要保持元素顺序的场景下非常有用,需要的朋友可以参考下

Set系列集合

  • List系列集合:添加的元素是有序、可重复、有索引
  • Set系列集合:添加的元素是无序、不可重复、无索引
    • 无序:存取顺序不一致
    • 不重复:可以去除重复
    • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

Set集合的实现类

  • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引
  • TreeSet:可排序、不重复、无索引

Set接口中的方法上基本上与Collection的API一致  

Collection

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的

方法名称说明
public boolean add(E e)把给定的对象添加到当前集合中
public void clear()清空集合中所有的元素
public boolean remove(E e)把给定的对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定的对象
public boolean isEmpty()判断当前集合是否为空
public int size()返回集合中元素的个数/集合长度

练习:存储字符串并遍历

利用Set系列的集合,添加字符串,并使用多种方式遍历

1。迭代器

2。增强for

3。Lambda表达式

public class A01_SetDemo1 {
    public static void main(String[] args) {
        //1.创建一个Set集合的对象
        Set<String> s = new HashSet<>();
        //2.添加元素
        boolean r1 = s.add("朵朵");
        boolean r2 = s.add("朵朵");
        //true
        System.out.println(r1);
        //false
        System.out.println(r2);
        //[朵朵]
        System.out.println(s);
        boolean r3 = s.add("小七");
        boolean r4 = s.add("钢镚");
        //无序
        //[钢镚, 朵朵, 小七]
        System.out.println(s);
        //迭代器遍历
        Iterator<String> it = s.iterator();
        while (it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }
        //增强for
        for (String str : s) {
            System.out.println(str);
        }
        //Lambda
        s.forEach(str -> System.out.println(str));
    }
}

HashSet

  • HashSet集合底层采用哈希表存储数据
  • 哈希表是一种对与增删改查数据性能都较好的结构

哈希表组成

  • JDK8之前:数组+链表
  • JDK8之后:数组+链表+红黑树

哈希值

对象的整数表现形式

  • 根据hashCode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点

  • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同地址值计算出来的哈希值也可能一样(哈希碰撞)
public class A02_HashSetDemo1 {
    public static void main(String[] args) {
        //1。创建对象
        Student s1 = new Student("朵朵", 5);
        Student s2 = new Student("朵朵", 5);
        //2。 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
        //1554874502
        System.out.println(s1.hashCode());
        //1846274136
        System.out.println(s2.hashCode());
        //重写hashCode后:不同的对象只要属性值相同,计算出的哈希值就是一样的
        //26209637
        System.out.println(s1.hashCode());
        //26209637
        System.out.println(s2.hashCode());
        //3。在小部分情况下,不同的属性值或者不同地址值计算出来的哈希值也可能一样(哈希碰撞)
        //96354
        System.out.println("abc".hashCode());
        //96354
        System.out.println("acD".hashCode());
    }
}

HashSet的底层原理

在这里插入图片描述

1.创建一个默认长度16,默认加载因子0.75的数组,数组名table

  • 加载因子:扩容时机
  • 当存入数据达到16 x 0.75 = 12时候,数组扩容成原数组的二倍
  • JKD8以后:当链表长度大于8而且数组长度大于等于64,当前链表自动转成红黑树,增加查找效率

2.根据元素的哈希值跟数组的长度计算出应存入的位置

  • int index = (数组长度 - 1) & 哈希值;
  • 如果集合中存储的是自定义对象,必须要重写hashCode和equsl方法

3.判断当前位置是否为null,如果是null直接存入(添加元素)

4.如果位置不是null,表示有元素,则调用equals方法比较属性值

  • 一样:不存 ------ 不一样:存入数组,形成链表
  • JDK8以前:新元素存入数组,老元素挂在新元素下面
  • JDK8以后:新元素挂在老元素下面

HashSet的三个问题

  • 问题1.HashSet为什么存和取的顺序不一样?
    • HashSet遍历是从数组的0索引开始,一条链表一条链表(或红黑树)的遍历,可能和存储时候的顺序不一样
  • 问题2.HashSet为什么没有索引?
    • HashSet不够纯粹,在底层是链表 数组 红黑树三种组合,定义谁都不合适。
    • 假设使用数组的索引,但一个索引处可能会有链表,不可能链表上所有的数据都是一个索引
  • 问题3.HashSet是利用什么机制保证数据去重复的
    • HashCode方法 --> 哈希值 --> 确定元素添加在哪个位置
    • equals方法 --> 比较内部的属性值是否相同(自定义对象要重写这两种方法)

练习:利用HashSet集合去除重复元素

需求:创建一个存储学生对象的集合,存储多个学生对象 使用程序实现在控制台遍历该集合

要求:学生对象的成员变量值相同,我们就认为是同一个对象

public class A02_HashSetDemo2 {
    public static void main(String[] args) {
        //1。创建3个学生对象
        Student s1 = new Student("朵朵", 5);
        Student s2 = new Student("小七", 7);
        Student s3 = new Student("钢镚", 9);
        Student s4 = new Student("朵朵", 5);
        //2。创建集合用来添加学生
        HashSet<Student> hs = new HashSet<>();
        //3。添加元素(重写hashCode和equal方法后)
        //true
        System.out.println(hs.add(s1));
        //true
        System.out.println(hs.add(s2));
        //true
        System.out.println(hs.add(s3));
        //false
        System.out.println(hs.add(s4));
        //4。打印集合
        //[Student{name = 小七, age = 7}, Student{name = 钢镚, age = 9}, Student{name = 朵朵, age = 5}]
        System.out.println(hs);
    }
}

LinkedHashSet

LinkedHashSet底层原理

  • 有序、不重复、无索引
  • 这里的有序指的是保证存储和取出的元素顺序一致
  • 原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序
public class A04_LinkedHashSetDemo {
    public static void main(String[] args) {
        //1。创建3个学生对象
        Student s1 = new Student("朵朵", 5);
        Student s2 = new Student("小七", 7);
        Student s3 = new Student("钢镚", 9);
        Student s4 = new Student("朵朵", 5);

        //2。创建集合用来添加学生
        LinkedHashSet<Student> lhs = new LinkedHashSet<>();

        //3。添加元素(重写hashCode和equal方法后)
        //true
        System.out.println(lhs.add(s1));
        //true
        System.out.println(lhs.add(s2));
        //true
        System.out.println(lhs.add(s3));
        //false
        System.out.println(lhs.add(s4));

        //4。打印集合
        //与添加顺序一样
        //[Student{name = 朵朵, age = 5}, Student{name = 小七, age = 7}, Student{name = 钢镚, age = 9}]
        System.out.println(lhs);
    }
}

数据去重

  • 默认使用HashSet
  • 如果要求去重且存取有序,才使用LinkedHashSet

到此这篇关于Java中的HashSet、LinkedHashSet集合解析的文章就介绍到这了,更多相关Java中的HashSet、LinkedHashSet内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • iOS socket网络编程实例详解

    iOS socket网络编程实例详解

    socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等。这篇文章主要介绍了iOS socket网络编程 ,需要的朋友可以参考下
    2017-03-03
  • Java线程编程中的主线程讲解

    Java线程编程中的主线程讲解

    这篇文章主要介绍了Java线程编程中的主线程,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • 使用java项目搭建一个netty服务

    使用java项目搭建一个netty服务

    这篇文章主要为大家详细介绍了如何使用java项目搭建一个netty服务,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-10-10
  • Spring Boot中Elasticsearch的连接配置原理与使用详解

    Spring Boot中Elasticsearch的连接配置原理与使用详解

    在Spring Boot中,我们可以通过Elasticsearch实现对数据的搜索和分析,本文将介绍Spring Boot中Elasticsearch的连接配置、原理和使用方法,感兴趣的可以了解一下
    2023-09-09
  • Spring各版本新特性的介绍

    Spring各版本新特性的介绍

    今天小编就为大家分享一篇关于Spring各版本新特性的介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • Mybatis中使用updateBatch进行批量更新

    Mybatis中使用updateBatch进行批量更新

    这篇文章主要介绍了Mybatis中使用updateBatch进行批量更新的相关资料,有逐条更新,sql批量更新等,具体实例代码大家参考下本文
    2018-04-04
  • Java网约车项目实战之实现抢单功能详解

    Java网约车项目实战之实现抢单功能详解

    本文详细介绍了如何使用Java实现网约车项目的抢单功能,并提供了一个完整的代码示例,以便读者能够直接运行和参考,感兴趣的朋友一起看看吧
    2024-12-12
  • Java中this关键字的4种用法详解(附常见面试题)

    Java中this关键字的4种用法详解(附常见面试题)

    在Java的编程世界里,this关键字宛如一把神奇的钥匙,看似简单,却蕴含着强大的功能,这篇文章主要介绍了Java中this关键字的4种用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-06-06
  • SpringBoot实现登录拦截的示例代码

    SpringBoot实现登录拦截的示例代码

    如果我们不进行登录拦截的话,即使我们跳过登录页面直接去访问任意一个页面也能访问成功,那么登录功能就没有意义,同时也会存在安全问题,本文就来介绍一下SpringBoot登录拦截,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • 详解Java正则表达式中Pattern类和Matcher类

    详解Java正则表达式中Pattern类和Matcher类

    java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。包括两个类Pattern和Matcher Pattern,Pattern是一个正则表达式经编译后的表现模式。Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查。
    2016-12-12

最新评论