探究java.util集合容器的底层原理

Java集合容器面试题(2026版) 序列号 内容 链接 1 Java基础知识面试题(2022版) https://blog.csdn.net/qq_43061290/article/details/124023797 2 Java集合容器面试题(2022版) 待续… https://editor.csdn.net/md?spm=1001.2101.3001.5352&articleId=124043363 3 Java异常面试题(2022版) 待续… 4 并发编程面试题 (2022版) 待续… 5 JV.... 阅读详情

java.util是包含集合框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组、日期Date类、堆栈Stack类、向量Vector类等)。集合类、时间处理模式、日期时间工具等各类常用工具包
在这里插入图片描述
java.util下的集合容器包含如图的几大类

set:无重复元素集合容器
List:允许有重复元素容器
Map:键值对

使用机制

在这里插入图片描述

Set

HashSet

HashSet的底层实现和HashMap有很多类似之处。
其抽象理解可以理解为,HashSet是HashMap中的键,之后进行遍历取出。
而HashMap本身的遍历其实违背了使用思想,而HashSet则很需要使用遍历。
HashSet的遍历:
1.iterator。
iterator的实现就是将容器的元素通过一张线性表串联起来,iterator就是指向表头的指针,具体为链表。
值得注意的是当HashSet改变时,需要重新申请iterator。
2.for(String s : hashmap) 通过for循环遍历。

当然对于碰撞和扩容,采取的方法同HashMap一样。
并且链表长度大于8时会自定转化为红黑树。

LinkedHashSet

LinkedHashSet继承了HashSet,而底层实现又和LinkedHashMap相似。
因为本身HashSet是无序的,散乱的数据。
而使用双向链表去维护,可以得到插入顺序、查询顺序

TreeSet

按照前面的例子,多半TreeSet和TreeMap一样实现方式,通过红黑树,插入和查询控制在o(logn)
在看了源码后果然如此。

List

ArrayList

ArrayList不是线程安全的,在多线程时使用需要上锁
ArrayList是基于数组实现的添加和查询指定项很快,但是插入和删除效率不高

初始时容量为0,在第一次扩容为10;
之后每次在add内存不够时扩容1.5倍。
private void grow(int minCapacity) {
int oldCapacity = elementData.length; // 旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量为旧容量的1.5倍
if (newCapacity - minCapacity < 0) // 新容量小于参数指定容量,修改新容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) // 新容量大于最大容量
newCapacity = hugeCapacity(minCapacity); // 指定新容量
// 拷贝扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}

LinkedList

LinkedList不是线程安全的
LinkedList是基于链表实现的插入和删除很快,但是添加效率不高

Vector

Vector是线程安全的
Vector也是基于数组实现,机制与ArrayList类似

Map

HashMap

JAVA中常用HashMap,在使用上看,HashMap对应一系列键值对,每个键唯一标识一个值。
HashMap使用了Hashing(哈希)原理。

Hash是一种伟大的索引方法。
在这里插入图片描述
hash函数将键的集合映射到hash集合,每一个键的元素唯一对应一个hash元素,例如
如果hash函数是hash(x)=x-1, 那么给定一个5,hash(5)=4,那么将第4列指针指向5的存储。
如果hash函数是hash(x)=x%5, 那么给定一个5,hash(5)=0,那么将第0列指针指向5的存储。

冲突(碰撞)
对于hash(x)=x%5而言,x=10,x=5得到的hash值相同,hash(5)=0,hash(10)=0。
此时就发生了冲突(碰撞)。
假设此时已经将第0列的指针指向了10的存储,又来了一个5。
此时通常有两种处理方法。
1) 链接地址
将后来的hash(a)=0,用上一个hash(x)=0指针a的存储。
即将10的存储指向5的存储。
在这里插入图片描述

2) 开放地址
将后来的hash(a)=0,用下一个0+1的指针a的存储。
如果下一个地址有指向,则继续向下。
在这里插入图片描述
扩容
扩容就是在原有效率过低时,用一个新的hash代替原有的hash函数,使得hash集合更大。
然后将原来的hash表用新的hash函数算后,转存到新的hash表中。
hash的容量使用率在75%以内效率都会很高,超过75%后,效率会明显下降,所以当容量使用率达到75%时需要扩容。
HashMap第一次的容量为16,当存储量达16*0.75=12时,就会是HashMap扩容,使得容量翻倍。

好处
hash的好处,不必排序,不必搜索,只需要计算就可以查询到某个值。
同样的对于数学中的高维问题,可以将hash函数设为降维方法。
比如对于一个六面体来说,普通的查询需要查询8个顶点坐标,才能确定一个六面体。
但是对于hash而言,可以设hash(六面体)=体积。
那么只需要对六面体做体积运算即可查询,大大的简化了查询。

hash函数的设计
某种程度上而言尽可能将大的集合转化为小的集合,使得查询更有效率。

在JDK中:
public class HashMap<K,V> extends AbstractMap<K,V>
可以看出HashMap继承AbstractMap。
public abstract class AbstractMap<K,V> implements Map<K,V>
而AbstractMap<K,V> 实现了 Map<K,V>接口
public interface Map<K, V>可以看出Map是一个接口。
其中K是Key键,V是Value值。
Map接口定义了:
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
V put(K key, V value);
V remove(Object key);

首先HashMap在实现中定义了
Node<K,V>,一个很简单的链表。
定义了:
final int hash; //哈希值
final K key; //键
V value; //值
Node<K,V> next; //下一个结点

定义了方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这个方法定义了hash的计算。
Object 中定义为:public native int hashCode();
hashCode()是一个native方法,意味着方法的实现和硬件平台有关,默认实现和虚拟机有关,对于有些JVM,hashCode()返回的就是对象的地址,大多时候JVM根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,并返回。

HashMap中定义了:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
而putVal则是将键值对保存,操作是:
根据hash(key) 寻找是否冲突
如果冲突,那么找到冲突的结点Node
遍历链表如果k-v存在,则修改v
不存在则找到最后一个结点即(Node.next==null) 使得next等于当前的key-value
如果不冲突,那么在hash表中加入该结点
而

对于get来说,就是很简单查询。
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}

在源码中可以看出,HashMap是通过连接地址来解决冲突(碰撞)的。
而当链表长度超过8时,HashMap会自动将链表转成红黑树。
在多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。

Hashtable

Hashtable和HashMap类似,源码中可以发现主要差别在于Hashtable的实现之前加了synchronized。
也就是说Hashtable是线程安全的。

至于其他更多的区别,可以读一下源码解读Hashtable

ConcurrentHashMap

ConcurrentHashMap是Java中的一个线程安全且高效的HashMap实现。
基本内容与HashMap相似,其中区别的,ConcurrentHashMap是通过CAS+synchronized的加锁。
另外当ConcurrentHashMap扩容时,p<0,此时任何线程都不能读写。

ConcurrentHashMap比起Hashtable的效率更快的原因是,Hashtable是将整个共享区上锁,所有资源都由一把锁控制。
而ConcurrentHashMap是用分段锁控制,每把锁控制一部分资源,这样当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。其中的操作比较复杂,详细可以读一下:
ConcurrentHashMap源码解读

LinkedHashMap

LinkHashMap 保存了记录插入的顺序,用Iteraor遍历时先得到的肯定是先插入的,遍历时比HashMap慢,有HashMap的全部特性
LinkedHashMap的实现是对HashMap加了一个双向链表作为维护,可以获得插入顺序 、 访问顺序。
同样LinkedHashMap和HashMap一样没有锁保护,线程不安全。
关于LinkedHashMap的双向链表具体实现,见LinkedHashMap底层原理

TreeMap

TreeMap是一个自动升序的Map。
在源码中可以发现TreeMap继承AbstractMap<K,V>,实现接口NavigableMap<K,V>
而AbstractMap<K,V>实现了Map<K,V>,而NavigableMap<K,V>实现了SortedMap<K,V>,而SortedMap<K,V>继承了Map<K,V>。

TreeMap的实现是基于红黑树的。
红黑树是自平衡二叉树,其结点必须为红色或者黑色,查询效率很高。
对于自动升序的Map而言,建立红黑树,再通过红黑树去实现SortedMap<K,V>,TreeMap来使用SortedMap<K,V>的接口,可以达到自动升序的效果。

在这里插入图片描述

红黑树:
红黑树是计算机科学内比较常用的一种数据结构,它使得对数据的搜索,插入和删除操作都能保持在O(lgn)的时间复杂度。
红黑树本质上是一颗二叉搜索树,它满足二叉搜索树的基本性质——即树中的任何节点的值大于它的左子节点,且小于它的右子节点。
一颗红黑树必须满足以下几点条件:
规则1、根节点必须是黑色。
规则2、任意从根到叶子的路径不包含连续的红色节点。
规则3、任意从根到叶子的路径的黑色节点总数相同。

而对于一颗红黑树,除了根节点插入为黑色外,其余的插入均为红色,一旦出现连续的红色则意味着不平衡需要调整。
红黑树的调整是旋转+着色。

Java核心库——java.util包深入解析与源码研究 Java集合框架(Java Collections Framework)是Java编程语言中一系列用于组织和操作对象集合的接口和类。它提供了一套性能优良、接口统一的集合工具,包括List、Set、Map等不同类型的集合实现。通过这些集合类,开发者可以高效地管理数据集合,无需关心底层的数据结构实现细节。在自定义类和接口中使用泛型可以增强代码的复用性并保证类型安全。泛型类可以被创建为具有通用类型参数的模板,这允许它们接受不同类型作为参数而无需改变类的结构。 阅读详情

相关推荐

【目标跟踪】ECO算法论文阅读:ECO: Efficient Convolution Operators for Tracking

这里所指的训练集是指保存了每一帧的跟踪结果的训练集,也就是说,每一次进行model update的时候,要用在这一帧之前所有跟踪到的样本。那么随着视频越来越长,这个训练集就会越来越大。因为当目标被遮挡或者丢失的时候,比较新的这些样本本身就是错的,那么模型很容易有model drift,就是被背景或者错误的目标污染,导致跟踪结果出错。如下图所示,Baseline是传统的训练集,每更新一帧就加一个进来,那么连续的数帧后训练集里面的样本都是高度相似的,即容易遗忘前面的样本,且容易对最近的若干帧样本过拟合。

qq_43799400的博客 2612

理解java.util包--容器相关

 java.util包中提供了一些列容器相关的接口和类。接口有:1 Collection:2 Set:继承自Collection接口,元素排列无序,不能重复3 List:继承自Collection接口,元素排列有序,可以重复4 Map:不继承自Collection接口,通过key-value形式存储元素,键值不可以重复5 Iterator:遍历使用的接口类:1

小猪的成长专栏 1745

Android Settings 设置Preference置灰显示

使Preference置灰显示,不能点击。先调用Preference的setEnabled(false),再调用setShouldDisableView(true); 如果使置灰项恢复正常可点击,可调用setEnabled(true)。

随风而动 1048

java.util.concurrent 包下容器简介

1.ConcurrentMap 接口 ConcurrentHashMap:替代 HashTable,并发性能大大提升的map ConcurrentSkipListMap:替代TreeMap 可以并发排序的Map ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一- 个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的...

临窗,听雨声 538

Python逆向爬取Tik Tok,MsToken,X-Bogus以及signature

tiktok作为字节海外的自媒体平台,具有很非常大的价值,本文旨在教会大家实现Tik Tok的签名获取以及数据的最终爬取。

weixin_74305707的博客 3270

SAP 小结

关于SAP 开发的实习就要告一段落了,从刚开始接触,到现在已经6个月了,想要很简要的把自己对SAP的理解写出来,便于以后查阅(不定期更新)。 1. 什么是SAP? 广义的SAP,可以指代SAP公司以及SAP的各种产品,它的意义要在特定语境下才能确定。一般我们所说的SAP特指SAP ERP。 SAP ERP(enterprise resource planning)是一个企业信息系统。通过这个系

Remy的博客 1953

[转帖]java.util常用容器类

 线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。     Collection  ├List  │├LinkedList  │├ArrayList  │└Vector  │ └Stack  └Set  Map  ├Hasht...

莫高雷草原 212

数据工程师——ETL

文章摘要 ETL(数据抽取、转换、加载)是数据处理的核心流程,负责将分散数据源整合为高质量数据。ETL与ELT的主要区别在于转换步骤顺序,分别适用于不同规模数据场景。数据仓库通过ETL从多个数据源集成数据,支持分析决策。维度建模中的事实表记录业务指标,维度表提供上下文信息。缓慢变化维度(SCD)处理随时间变化的维度数据,常见类型包括直接覆盖型、新增版本型等。数据清洗解决数据缺失、重复、错误等问题,确保数据质量。数据血缘记录数据流转路径,便于问题排查和影响分析。常用ETL工具如Kettle通过转换和作业实现数

weixin_45729710的博客 1559

基于java的容器底层原理实现

一.java容器的体系大览 1. java为啥子需要容器呢? 通常,程序总是在运行时才能确定要创建的对象,甚至是对象的类型。为了解决这个问题,需要在任意时刻任意位置创建任意数量的对象。大多数语言都提供某种方法来解决这个问题,Java使用容器来解决这个问题。容器也称集合类,基本的类型是List、Set、Queue、Map,但由于Java类库中使用了Collection关键字来代表某一接口,所以一般...

萌萌的PP博客 8641

随机变量序列 收敛 检验 matlab,利用MATLAB验证样条插值的收敛性

问题提出多项式插值是不收敛的,即插值的节点多,效果不一定就好。对样条函数插值又如何呢?理论上证明样条插值的收敛性是比较困难的,但通过本实验可以验证这一理论结果。.实验内容请按一定的规则分别选择等距或者非等距的插值节点,并不断增加插值节点的个数。考虑实验2.1中的函数或选择其他你有兴趣的函数。(中函数)。实验要求(1)随节点个数增加,比较被逼近函数和样条插值函数误差的变化情况。分析所得结果并与拉格朗...

weixin_30895121的博客 4735

VS Code/TRAE集成Cppcheck并启用MISRA规则

本文探讨了嵌入式开发中Cppcheck工具集成与MISRA规则检查的必要性。详细介绍了安装配置步骤:安装VSCode/TRAE、Cppcheck软件、配置插件参数、安装Python环境,并重点说明了MISRA规则文件的设置方法。通过完整的配置流程,实现了嵌入式开发环境下静态检查与安全标准的落地。

SDFangFang2025的博客 1282

深入整理java集合容器

前言 辛苦准备两三天,转载希望指明原处。 http://blogs.xzchain.cn java容器是每个java开发者必须打交道的东西,简单来说容器就是可以装载其他java对象的对象。自jdk1.2开始JCF(Java Collections Framework)为java开发者提供了通用的容器。 思考下为什么需要容器呢? 《thinking in java》书...

starlh35的博客 5149

ROS从入门到精通0-4:VSCode 搭建 ROS 工程环境

为了提高开发效率,可以先安装集成开发工具和使用方便的工具,本教程用VSCode搭建ROS工程环境

FRIGIDWINTER的博客 1万+

Java.util包简单总结

java.util包粗略总结

大树的学习之路 5万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

【集合类】 java.util.ConcurrentModificationException异常详解&HashMap&ConcurrentHashMap原理探究

文章目录1. 前言2. 回顾下ArrayList3. HashMap3.1 为什么HashMap是非线程安全的?3.2 HashMap迭代期间,可能引起ConcurrentModificationException异常3.3 使用ConcurrentHashMap替代HashMap3.3 ConcurrentHashMap迭代期间能执行remove等操作吗 1. 前言 我们之前写过关于ArrayList的线程安全相关的文章《java.util.ConcurrentModificationException

m0_45406092的博客 1934

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Java Iterator迭代器(用来遍历集合等容器) (java.util.Iterator<E>)

一.概念和使用方式 * 迭代:即Collection集合元素的通用获取方式。 * 在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。 * 一直把集合中的所有元素全部取出。 这种取出方式专业术语称为迭代。 1.1 Iterator接口 在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。 Iterator接口也是Java集合中的一员,但它与`Collection`、`Map`接口有所不

Beyond_Nothing 1481

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

java 并发集合_Java并发集合

Java并发集合并发集合实现1JDK1.5的出现,对于集合并发编程来说,java developer有了更多的选择。不过,在JDK1.5之前,Java也还是提供了一些解决方案。(1)最为简单直接的就是在程序中我们自己对共享变量进行加锁。不过,缺点也显而易见,手动实现线程安全间接增加了程序的复杂度,以及代码出错的概率---例如:线程死锁的产生;(2)我们还可以使用Java集合框架中的Vector、H...

weixin_34910865的博客 1720

JavaSE学习之集合框架 java.util.Collection、java.util.List、java.util.Set

集合框架 概念 概念:对象的容器,存储对象的对象,可代替数组。 特点:容器的工具类,定义了堆多个对象进行操作的常用方法。 位置:java.util.*; Collection体系集合 Collection代表一组对象,称之为集合 List接口的特点:有序、有小标、元素可重复。 Set接口的特点:无序、无下标、元素不能重复。 List接口与实现类:有序、有小标(index...

qq_36090621的博客 634

Java并发容器大合集

概述        java.util包中的大部分容器都是非线程安全的,若要在多线程中使用容器,你可以使用Collections提供的包装函数:synchronizedXXX,将普通容器变成线程安全的容器。但该方法仅仅是简单地给容器使用同步,效率很低。因此并发大师Doug Lea提供了java.util.concurrent包,提供高效的并发容器。并且为了保持与普通的容器的接口一致性,仍然使用ut

大闲人柴毛毛 8261

Java JUC并发集合详解:线程安全容器完全指南

Java并发容器(JUC)提供线程安全的高性能集合类,包括ConcurrentHashMap(分段锁/CAS优化)、阻塞队列(Array/LinkedBlockingQueue)、非阻塞队列(ConcurrentLinkedQueue)和写时复制集合(CopyOnWriteArrayList)。相比传统同步方式,JUC通过锁分离、CAS无锁算法等实现更高并发性。选型需考虑数据结构特性(Map/Queue/List)、是否需要有序/阻塞,以及读写比例,优先选用JUC而非同步包装类,以提升多线程环境下的程序性能

zyyyyyhhhhh的博客 1172

java集合与数据结构

一、.什么是集合? 集合的由来:通常,我们的程序需要根据程序运行时才知道创建多少个对象。但若非程序运行,程序开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型。为了满足这些常规的编程需要,我们要求能在任何时候,任何地点创建任意数量的对象,而这些对象用什么来容纳呢?我们首先想到了数组,但是数组只能放统一类型的数据,而且其长度是固定的,那怎么办呢?集合便应运而生了! Java集合类存放于 java.util 包中,是一个用来存放对象的容器。 注意:①集合只能存放对象。比如你存一个 int

qq_45849275的博客 107

常见的并发集合(java.util.concurrent)

基本介绍BlockingQueue是并发工具包 java.util.concurrent 中提供的一种线程安全的队列实现,它主要用于生产者-消费者模式中,为多线程应用提供了一种高效和安全的方式来管理共享资源。BlockingQueue 的主要特点是当队列满时,生产者线程会被阻塞,直到有消费者线程消费了队列中的元素,或者等待超时;同样,当队列空时,消费者线程会被阻塞,直到有生产者线程添加了新的元素,或者等待超时。

m0_68041576的博客 1115

Java集合系列之并发包(java.util.concurrent)下的高级集合类

先看一张描述java.util.concurrent包下集合组成结构的类图 下列介绍的集合统一特性:线程安全,支持并发操作 非阻塞队列(队列无数据,操作队列产生异常或返回null,不具备等待/阻塞的特色) ConcurrentHashMap:相对于线程安全的HashTable,优势在于HashTable不支持在循环(iterator)中对结构调整(增、删),否则会有ConcurrentM...

良月柒 6841

Java中集合ArrayList、LinkedList以及HashMap常用容器详解

Java中容器对象主要用来存储其他对象,根据实现原理不同,主要有3类常用的容器对象: 1、ArrayList 使用数组结构存储容器中的元素 2、LinkedList 使用链表结构存储容器中的元素 3、HashMap 使用Hash原理,同时使用数组和链表结构...

chenxuyuana的博客 6581

Java集合框架深度解析:从底层原理到实战选型指南

在Java编程中,数据结构是构建高效应用的核心基础。集合框架通过提供标准化的接口与实现分离设计,将算法与数据结构解耦,实现了代码的高复用性与可维护性。其核心价值在于为动态数据管理提供了高性能、类型安全的解决方案,广泛应用于数据处理、缓存系统和业务逻辑编排等场景。本文聚焦ArrayList与HashMap这两个高频使用的实现类,深入剖析其动态数组扩容与哈希表冲突解决机制,并结合线程安全、迭代器快速失败等工程实践要点,为开发者提供从原理到选型的完整知识图谱。

weixin_30376083的博客 409
上一篇: acm刷题记录(2020.3.15)
下一篇: 探究Java并发编程(持续更新)
不见冬
博客等级 码龄7年 3粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值