集合(List、Set、Map)ArrayList、LinkedList、Vector、HashSet、LinkedHashSet、HashMap

集合:

集合是动态保存多个任意对象,使用增加、删除元素时比较方便(自动扩缩容,不需要自己编写逻辑);

集合有单列集合Collection双列集合Map

其中Collection有两个子接口:Set、List;

List:有序、可以重复、支持索引、允许有多个null;

有三种遍历方式(迭代器、增强for、普通for (因为有索引));

List有实现类:ArrayList、LinkedList、Vector;

Set:无序、不可以重复、最多有一个null、无索引;

有两种遍历方式:迭代器、增强for;

取出的顺序和添加的顺序不一样,但只要取了 一次,每次都一样;

Set有实现类:HashSet、LinkedHashSet、TreeSet;

Map有三个实现类:HashMap、Hashtable、TreeMap;

保存的是具有映射关系的键值对key-value;

key和value可以是任何引用类型的数据,会封装到HashMap&Node中;

key不允许重复,value可以重复;

key可以为null,但只能有一个,value可以为null,可以有多个;

一对key-value保存到Node中,而Node又实现Entry接口,所以Map有三种遍历方式:

keySet:获取所有键的集合,用get(key)通过键获得对应的值;

values:获取所有值的集合,但无法获取对应的键;

entrySet:获取所有键值对的Set集合,每个元素都是一个Map.Entry对象,代表一个键值对;

**迭代器Iterator:**所有实现Collection接口的集合类都有一个iterator()方法,遍历集合,并返回一个实现了Iterator接口的对象,但他本身并不存放对象;

Iterator.hashNext():判断是否还有下一个元素;

Iterator.next():光标下移,并将下移后集合的元素返回;

注:在iterator.next()前必须先使用iterator.hashNext来判断一下受否还有下一个元素,否则将会抛出异常;

增强for就是简易版的迭代器;

ArrayList:

允许有null,可以多个null;

是由数组来实现数据存储的;

基本等同于Vector,除了ArrayList是线程不安全(但执行效率高),在多线程情况下不建议使用;

扩容机制:ArrayList中维护了一个Object类型的数组elementDate:

translent Object[] elementDate;其中translent表示短暂的,该属性不会被序列化;

当创建ArrayList对象时,如果使用的是无参构造器,则elementDate初始容量为0,第一添加时,会扩容到10,再次扩容时elementDate容量会扩容到10*1.5=15,以此类推;

如果使用的是指定大小的构造器,则elementDate初始容量为指定的大小,如果需要扩容时,则直接扩容为1.5倍,以此类推;

LinkedList:

底层维护了一个双向链表;

LinkedList中维护了两个属性first和last,其中first指向首节点,las指向尾节点;

每个节点(Node对象)里又维护了三个属性pre、next、item,其中通过pre指向前一个节点、next指向后一个节点、item指向实际数据元素;

LinkedList的添加、删除不是通过数组来完成的,而是通过调整节点引用来完成元素的添加和删除的,所以效率更高;

线程不安全,没有实现同步;

LinkedList和ArrayList比较:

ArrayList底层维护的是数组,因为有索引,所以改查效率更高一点;

LinkedList底层维护的是链表,因为增删可以直接通过修改链表的指向即可,所以增删的效率更高一点;

Vector:

底层也是一个对象数组:protected Object[] elementDate;

Vector是线程同步的(即线程安全),Vector中的操作方法synchronized;

扩容机制:创建Vector对象时,如果使用的是无参构造器,则elementDate初始容量为10,如果需要扩容时,会扩容到2倍,以此类推;

如果使用的是有参构造器,则elementDate的初始容量为指定的大小,如果需要扩容时,则直接扩容为2倍,以此类推;

HashSet:

底层是HashMap,HashMap底层是数组+链表+红黑树;

添加一个元素时,先通过计算得到哈希值,然后转变成数组的索引值,根据索引值来看存储数据表table中的对应位置是否已经有存放的元素:

如果没有则直接添加到数组中(链表的头节点);

如果有则调用equals()来比较:如果相同就放弃添加;

如果不相同则添加到链表的最后;

因为HashSet的底层是HashMap,所以当一条链表的元素个数达到8,且数组中的个数 >= 64,就会进行树化(红黑树);
扩展:

根据 Java 官方文档和 HashMap 的源码实现,链表转换为红黑树的条件是:

  • 链表长度 >= 8 :一个桶(bucket)中的链表长度达到了 8
  • 数组长度 >= 64HashMap 的数组长度(即桶的数量)已经达到了 64 或更大。

这两个条件必须同时满足,才会触发链表到红黑树的转换。如果其中一个条件不满足,即使链表长度达到了 8,也不会进行树化。它仍然还会保持链表的形式。

LinkedHashSet:

底层是一个LinkedHashMap,底 层维护了一个数组+双向链表;

每一个节点有属性before和after,这样就形成了双向链表;

在添加一个元素时,先求哈希值来确定元素位置,如果没有相同的则直接添加到双向链表中,如果相同则不添加(和HashSet一样):

如果相等:不执行任何操作,因为集合不允许重复元素;

如果不相等:将新节点插入到双向链表的尾部,并更新相关指针,如:

tail.after = newElement;

newElement.before = tail;

tail = newElement;

其中tail尾节点,newElement新添加的元素;

HashMap:

当添加相同的key时,则会覆盖原来的key-value,等同于 修改,但key不会被替换,value会被替换;

没有实现同步,线程不安全;

底层维维护了Node类型的数组table : Node<K,V>[] table 数组,默认为null;

扩容机制:当创建对象时,加载因子初始为0.75;

当添加元素时,通过key的哈希值来得出在table的索引,判断该索引处是否有元素:

如果没有直接添加;

如果有再判断key是否相等:

如果相等则覆盖(替换value);

如果key不相等则需判断是树结构还是链表结构并作出相应处理:

如果是链表:将新节点插入链表尾部;

如果是红黑树:按照红黑树规则插入新节点;

第一次添加需扩容table容量为16,临界值16*0.75=12;以后再扩容,table和临界值都扩大两倍32和24,以此类推;

当table大小>= 64且一条链上的元素个数超过默认值8,就会树化(红黑树),链表会转换为红黑树,以提高查找效率; 相反地,如果某个红黑树中的节点数减少到小于 6,它会被转换回链表,以节省空间;

HashSet 和 HashMap 的区别:

HashSet:

是 Java 集合框架中的一个类,它实现了 Set 接口。HashSet 不保证元素的顺序,并且不允许存储重复元素。它是基于 HashMap 实现的,具体来说,HashSet 使用 HashMap 的键来存储元素,而值则使用一个静态的对象(通常是一个 PRESENT 标记),因为 HashSet 只关心元素是否存在于集合中,而不关心它们关联的值。

使用场景:

当你需要一个不包含重复元素的集合时,HashSet 是一个很好的选择;

对于频繁的插入、删除和查找操作,HashSet 提供了高效的性能;

HashMap:

是 Java 集合框架中的一个类,它实现了 Map 接口。HashMap 存储键值对(key-value pairs),并且允许一个 null 键和多个 null 值。它不保证键值对的顺序,并且基于哈希表(hash table)实现。

使用场景:

当你需要一个键值对映射关系,并且需要高效地进行插入、删除和查找操作时,HashMap 是一个很好的选择;

它适用于大多数不需要保持插入顺序的场景;

功能差异:

HashSet 关注的是元素的存在性,不允许重复元素,也不关心元素之间的关联值;

HashMap 关注的是键值对的映射关系,允许一个 null 键和多个 null 值;

性能相似:

两者都依赖于哈希表结构,因此在插入、删除和查找操作上具有相似的性能特点;

Hashtable(了解即可):

基本和HashMap一样,但key和value都不能为null,否则会抛出NullPointerExceptioon异常;

是线程安全的;

它是一个更古老的类,最早出现在 Java 1.0 中。

历史原因:

Hashtable 是基于早期的 Java 设计理念,当时的设计目标是提供一种线程安全的哈希表实现,而不允许 null 键和值是为了简化内部实现和避免潜在的并发问题;

Hashtable 最初设计时,Java 还没有引入泛型(generics),并且对 null 的处理相对简单。为了确保线程安全和简化实现,Hashtable 决定不支持 null 键和值。

相关推荐
小万编程9 分钟前
【2025最新计算机毕业设计】基于SSM的医院挂号住院系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】
java·spring boot·毕业设计·计算机毕业设计·项目源码·毕设源码·java毕业设计
白宇横流学长10 分钟前
基于Java的银行排号系统的设计与实现【源码+文档+部署讲解】
java·开发语言·数据库
123yhy传奇10 分钟前
【学习总结|DAY027】JAVA操作数据库
java·数据库·spring boot·学习·mybatis
想要打 Acm 的小周同学呀15 分钟前
亚信科技Java后端外包一面
java·求职·java后端
勉灬之17 分钟前
封装上传组件,提供各种校验、显示预览、排序等功能
开发语言·前端·javascript
西猫雷婶3 小时前
python学opencv|读取图像(二十三)使用cv2.putText()绘制文字
开发语言·python·opencv
我要学编程(ಥ_ಥ)3 小时前
速通前端篇——JavaScript
开发语言·前端·javascript
lishiming03084 小时前
TestEngine with ID ‘junit-jupiter‘ failed to discover tests 解决方法
java·junit·intellij-idea
HEU_firejef4 小时前
设计模式——工厂模式
java·开发语言·设计模式
云计算DevOps-韩老师4 小时前
【网络云SRE运维开发】2024第52周-每日【2024/12/31】小测-计算机网络参考模型和通信协议的理论和实操考题
开发语言·网络·计算机网络·云计算·运维开发