【复习笔记-Basic01】List、Set、Map的区别

文章讨论了在Java中,作者通过实践发现Set集合在添加少量元素时似乎呈现有序,实际上这是由于哈希冲突和HashMap底层实现的拉链法导致的。作者解释了哈希表的工作原理以及Set、List和Map的区别。
摘要由CSDN通过智能技术生成

问题发现的背景

最近刚刚开启为应对暑期实习的第一轮Java复习,每遇到一个知识点我都习惯地手敲代码实现或者求证一下,而不是硬背内容。
而在复习到 List、Set、Map 之间的去别的时候,我对Set集合进行了初始化,往初始化后的对象添加了三个值 a、b、c,并分别利用迭代器和Stream方法对其遍历取值,发现无论我执行多少次,其输出结果都按照添加顺序进行输出:

通过迭代器获取:
a
b
c
通过Stream方法获取:
a
b
c

甚至我进行了多次遍历并记录结果,所输出结果依旧相同。
代码:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class HashSetOrderTest {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("a");
        set.add("b");
        set.add("c");

        // 多次遍历并记录结果
        for (int i = 0; i < 5; i++) {
            System.out.println("遍历结果 " + (i + 1) + ":");
            Iterator<String> iterator = set.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                System.out.println(element);
            }
            System.out.println();
        }
    }
}

结果:
遍历结果 1:
a
b
c

遍历结果 2:
a
b
c

遍历结果 3:
a
b
c

遍历结果 4:
a
b
c

遍历结果 5:
a
b
c

Set 是否是无序集合?

我们通过Java入门课就得知了 Set集合 是一个无序、不可重复地集合,那为什么在这里,它带给我们的感觉却是和 List 一样有序呢?
首先,答案是否定的;
当我们添加更多个元素,在进行求证的时候,就会发现,它每一次输出的结果是不相同的。但如果只是因为这个,并不值得我花十五分钟写一篇博客。。
我们得从中找出原因。
我们尝试阅读源码,可以发现,HashSet类的底层实现是直接使用了HashMap类
在这里插入图片描述
那么我们就可以从HashMap的实现中找出其原因,我们可以先来明确下哈希表的基本定义:

哈希表是一种键值对的数据结构,它使用哈希函数将键映射到值。哈希函数将键转换为一个整数,该整数用于确定值在哈希表中的位置。

在可以确定哈希表的基本定义后,这个问题就转变成一个哈希问题了。而到这里,我就不得不联想到了哈希冲突。

  • 首先,我们知道造成哈希冲突的原因是不同的元素存储在哈希表的同一个位置而导致的。
  • 其次,在Java中,HashSet类使用HashMap类作为其底层实现。HashMap类使用拉链法来解决哈希冲突。拉链法是指将具有相同哈希值的键存储在链表中。
  • 接着,如果两个或多个元素具有相同的哈希值,则它们将存储在哈希表的同一个位置。那么进行读取的时候,它就会在同一个位置上任意取出一个值作为下一个输出的值,这就导致了在较多元素的Set集合中,每次读出的结果不同。
  • 最后,我们可以得知,当在越小的Set集合中,哈希冲突的可能性越小,所以当我们只有三个元素的时候,且他们并没有发生哈希冲突的时候,自然每次读取的顺序不会改变。

因此,在JDK的实现中,对于较小(拥有较少元素)的HashSet,每次读出的结果都相同,这是因为Set集合的底层实现使用了哈希表。Set集合中的元素通常会存储在哈希表的不同位置。这意味着,iterator()方法和stream()方法遍历集合时,会以相同的顺序访问元素。

以下示例说明了哈希表如何存储 Set集合中的元素:

Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("c");

// 使用HashMap类查看Set集合的底层实现
HashMap<String, Object> map = (HashMap<String, Object>) set;

// 遍历HashMap中的键值对
for (Map.Entry<String, Object> entry : map.entrySet()) {
    System.out.println(entry.getKey());
}

List、Set、Map的区别

最后,怕你们觉得这个问题很无聊,特此供上三个集合的区别汇总表。(但我个人是认为这样的复习方式挺有意思的,既让我对集合之间的区别有了更深的理解,也让我同时去复习到了哈希表、哈希冲突、哈希算法)

特性ListSetMap
接口CollectionCollection
元素存储有序、可重复无序、不可重复无序、键不可重复
遍历方式for循环、迭代器迭代器迭代器、entrySet()
常用方法add()get()remove()
size()contains
indexOf()lastIndexOf()
add()remove()
contains()
isEmpty()size()
put()get()remove()
containsKey()containsValue()
isEmpty()size()entrySet()
使用场景存储有序元素存储无序、不重复元素存储键值对
  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值