当前位置: 首页 > news >正文

Java——集合

目录

1.Java中的集合类

2.List

2.1ArrayList和LinkedList有什么区别?

ArrayList和LinkedList的用途的不同?

ArrayList和LinkedList是否支持随机访问?

ArrayList和LinkedList内存占用有何不同?

链表和数组有什么区别?

2.2ArrayList的扩容机制了解吗?

2.3快速失败fail-fast了解吗?

2.3什么是安全失败(fail-safe)呢?

2.4有哪几种实现ArrayList线程安全的方法?

2.5ArrayList和Vector的区别?

2.6CopyOnWriteArrayList了解多少?

3.Map

3.1可以说一下HashMap的底层数据结构吗?

3.2你对红黑树了解多少?

为什么不用二叉树?

为什么不用平衡二叉树?

为什么用红黑树?

红黑树是怎么保持平衡的?

3.3HashMap的put流程知道吗?

只重写元素的equals方法没有重写hashCode,put的时候会发生什么?

3.4HashMap怎么查找元素的呢?

13.HashMap的hash函数是怎么设计的?

14.为什么hash函数能减少哈希冲突?

15.为什么HashMap的容量是2的幂次方?

16.如果初始化HashMap,传一个17的容量,它会怎么处理?

18.讲解哈希冲突有哪些方法?

20.HashMap扩容发生在什么时候呢?

21.HashMap的扩容机制了解吗?

22.JDK8对HashMap做了哪些优化呢?

23.你能自己设计实现一个HashMap吗?


1.Java中的集合类

Java中的集合类主要分为两大类:CollectionMap接口。前者是存储对象的集合类,后者存储的是键值对(key-value)。

Collection接口又分为List,Set和Queue接口。每个接口有其具体实现类。以下主要的集合类:

PS:可尝试结合类图,对常见的实现类关系重点记忆。

2.List

2.1ArrayList和LinkedList有什么区别?

ArrayList是基于数组实现的,LinkedList是基于链表实现的。

ArrayList和LinkedList的用途的不同?

ArrayList和LinkedList是否支持随机访问?

ArrayList和LinkedList内存占用有何不同?

链表和数组有什么区别?

2.2ArrayList的扩容机制了解吗?

超过当前数组,1.5倍

2.3快速失败fail-fast了解吗?

fail——fast是Java集合的一种错误检测机制。

在用迭代器遍历集合对象时,如果线程A遍历过程中,线程B对集合对象的内容进行了修改,就会抛出Concurrent Modification Exception。

迭代器在遍历时直接访问集合中的内容。

并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用 hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改 modCount 值刚好又设置为了 expectedmodCount 值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的 bug。

java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),比如 ArrayList 类。

2.3什么是安全失败(fail-safe)呢?

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。


原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发 Concurrent Modification Exception。


缺点:基于拷贝内容的优点是避免了 Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。


场景:java.util.concurrent 包下的容器都是安全失败,可以在多线程下并发使用,并发修改,比如 CopyOnWriteArrayList 类。

2.4有哪几种实现ArrayList线程安全的方法?

在 Java 中,RandomAccess是一个标记接口(Marker Interface),它本身不包含任何方法,仅用于标识实现该接口的集合类支持快速随机访问操作。

常用的有两种

可以使用 Collections.synchronizedList() 方法,它可以返回一个线程安全的 List。
java

SynchronizedList list = Collections.synchronizedList(new ArrayList());
内部是通过 synchronized 关键字

加锁来实现的。
也可以直接使用 CopyOnWriteArrayList

,它是线程安全的 ArrayList,遵循写时复制的原则,每当对列表进行修改时,都会创建一个新副本,这个新副本会替换旧的列表,而对旧列表的所有读取操作仍然在原有的列表上进行。
java

CopyOnWriteArrayList list = new CopyOnWriteArrayList();
通俗的讲,CopyOnWrite 就是当我们往一个容器添加元素的时候,不直接往容器中添加,而是先复制出一个新的容器,然后在新的容器里添加元素,添加完之后,再将原容器的引用指向新的容器。多个线程在读的时候,不需要加锁,因为当前容器不会添加任何元素。这样就实现了线程安全。

2.5ArrayList和Vector的区别?

2.6CopyOnWriteArrayList了解多少?

3.Map

3.1可以说一下HashMap的底层数据结构吗?

JDK8中HashMap的数据结构是数组+链表+红黑树

数组用来存储键值对,每个键值对可以通过索引直接拿到,索引是通过对键的哈希值进行进一步的hash()处理得到的。

当多个键进过哈希处理后得到相同的索引时,需要通过链表来讲解哈希冲突——将具有相同索引的键值对通过链表存储起来。

不过链表过长时,查询效率会比较低,于是当链表的长度超过8时(且数组的长度大于64),链表就会转换为红黑树。红黑树的查询效率是O(logn),比链表的 O(n) 要快。

hash() 方法的目标是尽量减少哈希冲突,保证元素能够均匀地分布在数组的每个位置上。

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

如果键的哈希值已经在数组中存在,其对应的值将被新值覆盖。

HashMap 的初始容量是 16,随着元素的不断添加,HashMap 就需要进行扩容,阈值是capacity * loadFactor,capacity 为容量,loadFactor 为负载因子,默认为 0.75。

扩容后的数组大小是原来的 2 倍,然后把原来的元素重新计算哈希值,放到新的数组中。

3.2你对红黑树了解多少?

为什么不用二叉树?

为什么不用平衡二叉树?

为什么用红黑树?

链表的查找时间复杂度n,红黑树是一种折中方案,查找,插入,删除的时间复杂度都是O(log n)

红黑树是怎么保持平衡的?

3.3HashMap的put流程知道吗?

在Java中,HashMap的put方法用于将键值对存储在哈希表中。以下是HashMap的put方法的详细流程:

1.计算hash值:

首先调用key的hashCode()方法获取key的hash码,然后通过HashMap内部的hash方法对哈希码进行扰动计算,以减少哈希冲突。

2.确定桶的位置:

根据计算得到的哈希值,通过(n - 1)&hash(n为哈希表的容量,是2的幂次方)来确定该键值对应该存储在哪个桶(数组的哪个位置)中。例如,如果哈希表容量n = 16,那么(16 - 1) & hash相当于hash % 16,但按位与操作效率更高。

3.检查桶是否为空:

如果桶为空,直接在该位置创建一个新的节点(链表的头节点)并插入键值对。

if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);
4.处理桶不为空的情况:
  • 检查是否为相同节点:如果桶不为空,首先检查桶的第一个节点(链表头节点)的键是否与要插入的键相同。如果相同,则直接更新值。
  • if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;

  • 处理链表或红黑树:如果桶中的节点不是要找的节点,且该桶是链表结构,则遍历链表查找是否有相同键的节点。如果找到,则更新值;如果遍历链表未找到,则在链表末尾插入新节点。

  • 如果链表长度大于等于TREEIFY_THRESHOLD(默认值为 8)且哈希表容量大于等于MIN_TREEIFY_CAPACITY(默认值为 64),链表会转换为红黑树结构,通过红黑树的插入操作来插入新节点。

5.更新操作:

如果在上诉过程中找到键的节点,则更新该节点的值,并返回旧值。

if (e!= null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;
}
6.增加元素个数并检查扩容:

如果没有找到相同键的节点,说明是新插入的键值对,HashMap的元素个数size增加。然后检查是否需要扩容。如果size大于等于threshold(threshold = loadFactor * capacityloadFactor默认值为 0.75),则进行扩容操作。扩容会创建一个新的更大的哈希表,并将旧哈希表中的所有键值对重新计算哈希并插入到新哈希表中。

++modCount;
if (++size > threshold)resize();
afterNodeInsertion(evict);
return null;

3.4HashMap怎么查找元素的呢?

通过哈希值定位索引——》定位桶——》检查第一个节点——》遍历链表或红黑树查找——》返回结果。

13.HashMap的hash函数是怎么设计的?

14.为什么hash函数能减少哈希冲突?

15.为什么HashMap的容量是2的幂次方?

HashMap采用2的n次方倍作为容量,主要是为了提高哈希值的分布均匀性和哈希计算的效率。

HashMap通过(n - 1)&hash来计算元素的存储索引位置,这种位运算只有在数组容量是2的n次方时才能确保索引均匀分布。位运算效率高于取模运算,提高哈希计算的速度。

HashMap扩容时,通过容量为2的n次方,扩容时只需要通过简单的位运算判断是否需要迁移,减少重新计算哈希值的开销,提升了rehash的效率。

16.如果初始化HashMap,传一个17的容量,它会怎么处理?

18.讲解哈希冲突有哪些方法?

再hash,开放地址法和拉链法

20.HashMap扩容发生在什么时候呢?

21.HashMap的扩容机制了解吗?

HashMap的扩容是负载因子来决定的,扩容为原来的两倍

rehashing:

jdk1.7之前

jdk1.8之后优化:

16:010000  32:100000

15:001111   31:011111

拿老数组长度判断高位是否是1,

ctrl + F12

属性

内部类

创建一个空参对象只会给负载因子赋值,底层数组没有赋值,添加第一个元素才会创建

源码讲解:

table 哈希表结构中数组的名字

Default_initial_capacity; 数组默认长度16;

DEFAULT_FACTOR; 默认加载因子0.75;

创建对象

添加元素

数组位置为null

数组位置不为null,键不重复挂在下面形成链表或红黑树

数组位置不为null,键重复,元素覆盖

22.JDK8对HashMap做了哪些优化呢?

1.红黑树,优化查询性能,避免链表过长

2.优化扰动函数,

3.优化扩容机制

4.头插法变为尾插法

23.你能自己设计实现一个HashMap吗?

24.HashMap是线程安全的吗?

25.怎么解决HashMap线程不安全的问题呢?

26.CopyOnWriteArrayList和Collections.synchronizedList有什么区别?分别有什么优缺点?


文章转载自:

http://406GV5QV.gnmhy.cn
http://RvbC4Rph.gnmhy.cn
http://h3SmDQ7w.gnmhy.cn
http://91c36jqc.gnmhy.cn
http://RYYShzxK.gnmhy.cn
http://hXqPl0xK.gnmhy.cn
http://yjcJYKAv.gnmhy.cn
http://PtOyA7Uy.gnmhy.cn
http://DdYVPQQm.gnmhy.cn
http://NWQHyXjf.gnmhy.cn
http://s1fn0LK8.gnmhy.cn
http://W7pJIT2p.gnmhy.cn
http://1lgt4B0B.gnmhy.cn
http://b0qlr3aY.gnmhy.cn
http://igOKid3V.gnmhy.cn
http://LWJvhcP8.gnmhy.cn
http://5ZnXPi37.gnmhy.cn
http://EUvBakrF.gnmhy.cn
http://AV4QCYJm.gnmhy.cn
http://ZbA5ImKV.gnmhy.cn
http://PQDkqcQp.gnmhy.cn
http://NZASyJEX.gnmhy.cn
http://6p8rJc36.gnmhy.cn
http://ZBwJd6UF.gnmhy.cn
http://DkSGfZ5v.gnmhy.cn
http://AUFQX5fA.gnmhy.cn
http://6t6dmctA.gnmhy.cn
http://VKHjpHnI.gnmhy.cn
http://BrgpH0mh.gnmhy.cn
http://MXI2pduz.gnmhy.cn
http://www.dtcms.com/a/385089.html

相关文章:

  • AI 重塑制造业:智能质检降本 30%、预测性维护减少停机,传统工厂的 “智改” 路径
  • CKS-CN 考试知识点分享(7) 网络策略 Deny和Allow
  • 已收货数量与已出货数量不一致,不能关闭订单
  • Spring 框架从入门到精通(第二篇)—— 依赖注入(DI)与 AOP 面向切面编程
  • 《虚拟机 ping www.baidu.com失败?Linux ifcfg-ens33 网络配置实操教程》
  • 【LangChain指南】样例选择器(Example selectors)
  • 《深入剖析Kubernetes》02 崭露头角
  • Spring Boot日志
  • 跨域(CORS)和缓存中间件(Redis)深度解析
  • 010SecMain_InitializeDebugAgentPhase2
  • 检索融合方法- Distribution-Based Score Fusion (DBSF)
  • 排序实现java
  • 聊聊测试策略与测试方案
  • 考察软件售后服务,保障线上招标采购管理软件高效运行
  • 云HIS系统源码(HIS+LIS+EMR全套源码)门诊/住院/医保全流程打通
  • 单例模式,加锁
  • CV论文速递 | 13篇前沿论文精选:生成与处理、3D视觉、医学影像等核心方向(09.08-09.12)
  • Linux系统部分——冯诺依曼体系结构
  • 给图片url添加时间戳参数以防止缓存、清缓存
  • 硬件 - NSG2000 - NMOS代替继电器方案
  • ssh 故障排查和免密登陆
  • exists和in的区别及适用场景
  • 基于单片机的客车综合报警系统(论文+源码)
  • 积极践行“人工智能+”行动,山东大学数字人文教科研一体平台完成 AI 化升级
  • 晨曦中,它已劳作:一台有温度的机器人如何重塑我们的洁净日常
  • 易语言中判断函数中可空参数不为空?
  • 2025机器人打磨抛光设备推荐及汽车零件/铸件打磨机器人技术解析
  • ESP32三种主流的开发环境
  • GTPU涉及NR RAN 容器
  • 【数值分析】02-绪论-误差