三水网站建设首选公司,wordpress d9,做视频剪辑接私活的网站,网站后台素材集合 1.HashMap底层#xff1f;扩容机制#xff1f;1.7-1.8的升级#xff1f;2.HashMap的长度为什么是2的幂次方#xff1f;3.HashMap 插入1.7和1.8的区别#xff1f;4.什么是红黑树#xff1f;O(logn)5.HashMap为什么会使用红黑树#xff1f;6.ArrayList底层#xff1… 集合 1.HashMap底层扩容机制1.7-1.8的升级2.HashMap的长度为什么是2的幂次方3.HashMap 插入1.7和1.8的区别4.什么是红黑树O(logn)5.HashMap为什么会使用红黑树6.ArrayList底层扩容机制7.LinkedList底层扩容机制8.ArrayList可以序列化但是为什么不直接序列化9.数组和ArrayList的区别有数组为啥还有ArrayList10.ConcurrentHashMap11.HashMap的put的理解12.HashMap的get的理解13.HashSet的底层14.HashSet是如何保证数据不重复的15.TreeMapHashMapHashTableLinkedHashMap区别16.HashSetLinkedHashSet?TreeSet?区别17.什么是Hash什么是Hash冲突怎么解决的18.什么是扰动函数19.集合中的快速失败机制fail-fast和安全失败20.怎么确保一个集合不能被修改21.Iterator和ListIterator区别22.移除collection元素23.遍历list的不同方式24.ArrayList为什么不直接序列化其内部数组25.快排时间复杂度26.什么叫序列化27.和equals的区别28.14是什么意思29.时间复杂度的排序30.HashMap中string、Integer这样的包装类适合作为key么任何类都可以作为key么Object作为HashMap的Key应该怎么做31.Collection和Collections的区别32.Collections.sort的原理33.怎么确保一个集合不被修改34.哪些集合类是线程安全的 持续更新中~ 1.HashMap底层扩容机制1.7-1.8的升级
底层数据结构 1.7是数组链表1.8底层是数组链表红黑树 默认大小16怎么扩容 创建一个新Entry空数组是原先的两倍遍历原有的数组把之前的数组重新Hash到新数组中 什么时候扩容负载因子是0.75f比如100数量到76的时候就扩容
2.HashMap的长度为什么是2的幂次方
为了能让HashMap存取高效尽量减少碰撞就是要尽量把数据分配均匀每个链表/红黑树的长度大致相同。
3.HashMap 插入1.7和1.8的区别
在 Java 1.7 中HashMap 使用了数组和链表来存储键值对。插入操作大致分为以下几个步骤
计算哈希值首先使用键的 hashCode() 方法计算其哈希值。定位桶通过哈希值与数组长度减 1 进行位与操作确定键应该放入哪个桶中。处理哈希冲突如果桶中已经有元素就遍历链表查找是否有相同的键。 如果有相同的键则更新对应的值。如果没有相同的键则在链表末尾添加新的键值对。 扩容如果链表长度大于阈值数组长度的 0.75 倍则对 HashMap 进行扩容并重新计算所有键的哈希值。
在 Java 1.8 中HashMap 对插入过程进行了优化引入了红黑树来处理链表过长的情况。插入操作的大致步骤如下
计算哈希值同样使用键的 hashCode() 方法计算其哈希值。定位桶通过哈希值与数组长度减 1 进行位与操作确定键应该放入哪个桶中。处理哈希冲突 如果桶中已经有元素并且元素是链表则遍历链表查找是否有相同的键。 如果有相同的键则更新对应的值。如果没有相同的键则在链表末尾添加新的键值对。如果链表长度大于 8并且数组长度大于 64则将链表转换为红黑树。 如果桶中已经有元素并且元素是红黑树则在红黑树中插入新的键值对。 扩容如果 HashMap 的大小超过了阈值数组长度的 0.75 倍则对 HashMap 进行扩容并重新计算所有键的哈希值。 从 Java 1.7 到 Java 1.8HashMap 的插入过程在处理哈希冲突时有所不同。Java 1.7 使用链表来处理冲突而Java 1.8 引入了红黑树来优化链表过长的情况。这种优化可以减少查找时间提高 HashMap 的性能。 4.什么是红黑树O(logn)
红黑树是一种不严格平衡二叉树不追求绝对的平衡允许局部不平衡根节点是黑色叶子节点是黑色的时候是空节点叶子节点不存数据相邻节点不能同时为红色红黑被隔开每个节点到达其可达叶子节点的所有路径含相同数目的黑色节点。
5.HashMap为什么会使用红黑树
利用链表对内存的使用率以及红黑树的高效检索是一种有效的数据结构对于AVL来说是一种高度平衡的二叉树查找效率高但维持这种高度平衡每次插入删除都要调整复杂耗时所以用红黑树。
6.ArrayList底层扩容机制 底层是数组实现的是一种随机访问模式实现了RandomAccess接口所以查询特别快 线程不安全的 查询快删除更新慢 默认长度是10也可以指定长度 扩容方式 重新定义nn/2长度的数组然后把原数组数据原封不动复制到新数组中再把原数组的地址换到新数组里 新增 先做一个长度判断长度不够是需要扩容的采用位运算右移一位在第n位增加一个数n后所有的元素放在n1的位置 ArrayList不会初始化数组大小 插入删除一定会慢么 取决于离数组末端有多远 怎样实现删除 删除第n个位置就是复制n1后的元素放到n的位置就是覆盖了。 ArrayList不适合做队列队列是先入先出涉及到数据迁移耗费性能
7.LinkedList底层扩容机制
底层是双向链表1.6之前是循环链表线程不安全的插入删除快查询慢不存在扩容问题会在需要的时候动态扩容链表需要内存比数组多 双向链表是指每个数据节点都有两个指针分别指向直接后继和直接前驱所以双向链表中的任意一个节点都可以很方便的访问它的前后。 8.ArrayList可以序列化但是为什么不直接序列化
出于效率考虑长度100但是实际只用50剩下50不用序列化提高效率节省空间。
9.数组和ArrayList的区别有数组为啥还有ArrayList
数组是一种固定长度的数据结构可以存储相同类型的元素并通过索引访问和修改元素。数组的主要优点是在访问元素时具有较高的性能因为可以通过索引直接定位元素。然而数组也有一些限制。首先数组长度固定无法动态调整大小这意味着一旦数组创建后无法添加或删除元素。其次数组不提供内置的方法来执行常见的操作如添加、删除、搜索等需要手动编写代码来处理这些操作。
ArrayList是Java集合框架中的一个类它是基于数组实现的动态数组。与数组不同ArrayList的长度是可变的可以根据需要动态调整大小。ArrayList提供了一组方便的方法来添加、删除、搜索和访问元素使操作更加简单和灵活。此外ArrayList还提供了各种实用的方法如排序、查找、替换等使数据操作更加方便。另一个重要的区别是数组可以存储基本类型和对象类型而ArrayList只能存储对象类型。如果需要存储基本类型可以使用对应的包装类来存储在ArrayList中。
10.ConcurrentHashMap
ConcurrentHashMap是Java并发包java.util.concurrent中的一个类它提供了线程安全的HashMap实现。ConcurrentHashMap的底层实现相当复杂主要是为了提供高并发、高性能的线程安全Map。 底层
1.7-分段数组链表1.8-Node数组链表红黑树synchronizedcas
ConcurrentHashMap 在 Java 8 中的底层结构是一个分段锁的实现具体来说是基于分段锁Segmentation和哈希表Hash Table的结合。它通过将整个哈希表划分为多个小的段Segment每个段都维护自己的锁从而实现并发的读写操作。
分段锁Segmentation
在 Java 8 中ConcurrentHashMap 默认将哈希表划分为 16 个段Segment每个段都是一个独立的哈希表有自己的锁。当线程访问某个键时会先根据哈希码定位到具体的段然后对该段加锁。由于不同线程可能访问不同的段因此它们之间不会互相阻塞从而提高了并发性能。
哈希表Hash Table
每个段内部都是一个标准的哈希表使用链表或红黑树来解决哈希冲突。当链表长度超过一定阈值默认为 8时链表会转换为红黑树以提高搜索性能。
读写操作
读操作读操作不需要加锁因为多个线程可以同时读取同一个哈希表中的数据。写操作写操作需要加锁但是只锁定需要操作的段而不是整个哈希表。当线程需要对某个键进行写操作时它会先根据哈希码定位到具体的段然后对该段加锁。其他线程可以同时访问其他段从而实现并发写操作。
Java 8 的改进
在 Java 8 中ConcurrentHashMap 引入了一些优化措施包括
减少锁的粒度通过将整个哈希表划分为更小的段可以减少锁的粒度从而提高并发性能。使用 CAS 操作在一些场景下ConcurrentHashMap 使用 CASCompare-And-Swap操作来更新节点的值而不是直接加锁。这可以进一步提高并发性能。红黑树优化当链表长度超过一定阈值时链表会转换为红黑树以提高搜索性能。 ConcurrentHashMap 在 Java 8中的底层结构是基于分段锁和哈希表的结合通过划分哈希表为多个小的段每个段维护自己的锁从而实现并发的读写操作。同时通过引入 CAS操作和红黑树优化等措施进一步提高了并发性能和搜索性能。 11.HashMap的put的理解
当put的时候用key的hash方法计算出hashcode放在bucket里位置是bucketIndex但是可能存在多个元素找到相同的位置这时候就会发生hash碰撞了当碰撞发生会取到bucketIndex已存储的元素最终通过equals比较Hashmap 通过hashcode和equals最终判断key是否已经存在存在就替换不存在则插入。
当链表长度超过8后将后面的数据存到红黑树中加快检索每个节点有一个存储位表示节点颜色红或者黑对任意一条从根节点到叶子节点的路径上各个节点着色方式限制红黑树保证没有任意一条从根到叶子的路径超过最短路径的两倍是一种弱平衡二叉树对于AVL来说红黑树旋转次数少对于搜索插入删除多用红黑树当元素少于8的时候查询成本高不用红黑树。
12.HashMap的get的理解
首先get方法接收一个键作为参数这个键是我们想要从HashMap中检索其对应值的键。
第一步计算哈希值。HashMap会首先调用该键对象的hashCode()方法来计算它的哈希值。这个哈希值是一个整数用于确定键在HashMap中的存储位置。
第二步定位桶。HashMap内部维护了一个数组这个数组的每个元素称为一个桶。HashMap会使用计算出的哈希值来定位到具体的桶。这通常是通过将哈希值与HashMap的容量capacity减1进行位与操作来实现的。
第三步处理哈希冲突。由于不同的键可能产生相同的哈希值这种情况称为哈希冲突所以HashMap在每个桶中维护了一个链表在Java 8及以后的版本中当链表长度超过一定阈值时会转换为红黑树。get方法会遍历这个链表或红黑树比较链表中的每个键是否与我们要查找的键相等。
第四步返回对应的值。如果在链表或红黑树中找到了与给定键相等的元素get方法会返回该元素对应的值。如果没有找到匹配的键那么get方法会返回null。
这个过程中HashMap利用了哈希表的数据结构通过计算哈希值和链表或红黑树来高效地存储和检索键值对。同时HashMap也进行了一些优化比如当链表过长时转换为红黑树以提高搜索性能。
13.HashSet的底层
是基于Hashmap实现的HashSet的值存放于HashMap的key上HashMap的value统一为PRESENT因此HashSet的实现比较简单相关HashSet的操作基本都是调用HashMap的相关方法来实现的HashSet不允许重复的值 HashSet速度慢于HashMap。
14.HashSet是如何保证数据不重复的
HashSet中的add()方法会使用HashMap的put方法HashMap的key是唯一的Hashset的key作为HashMap的key添加进去的所以跟HashMap一个逻辑key不能重复
15.TreeMapHashMapHashTableLinkedHashMap区别 HashMap 非线程安全初始16扩容2n1键值允许为null只允许一个 TreeMap 自然顺序排序底层是红黑树 HashTable(不建议用此类) 线程安全用synchronized修饰数组链表初始11扩容2n LinkedHashMap 链表按顺序迭代比HashMap快 ConcurrentHashMap 键值不允许为null线程安全1.7-分段数组链表1.8-Node数组链表红黑树synchronizedcasvolatile
16.HashSetLinkedHashSet?TreeSet?区别
17.什么是Hash什么是Hash冲突怎么解决的
Hash被翻译为散列就是把任意长度的输入通过散列算法变成固定长度的输出该输出就是散列值。
Hash冲突就是当输入两个不同的输入值根据同一散列函数计算出相同的散列值的现象我们就把它叫做哈希碰撞。
解决办法
使用链地址法(使用散列表)来链接有相同hash值得数据使用2次扰动函数(哈希函数)来降低哈希冲突的概率使得数据分布更均匀引用红黑树进一步降低遍历的时间复杂度使得遍历的更快
18.什么是扰动函数
哈希算法中的扰动函数是一种用于改善哈希值均匀性的技术。这种函数通常用于对输入的原始哈希码进行处理以消除潜在的模式、增加随机性并确保哈希值在哈希表中更均匀地分布。扰动函数的设计目的在于确保输出哈希值的均匀性、随机性和减少规律性以降低哈希冲突的发生概率并提高哈希表的性能和效率。
常见的扰动算法包括位运算如位移、按位与、按位异或等、乘法哈希通过乘以一个常数因子并提取整数部分来实现以及混合操作结合位运算、乘法和其他操作。这些扰动算法可以使得相似的输入产生不同的输出增加哈希值的随机性并使得哈希码在哈希表中分布更加均匀减少多个键被映射到同一个桶的可能性。
例如一种常见的扰动函数是将哈希码的高16位与低16位进行异或运算以此来加大低位的随机性减少哈希碰撞并优化散列效果。
19.集合中的快速失败机制fail-fast和安全失败
快速失败fail-fast机制是一种错误检测机制主要应用于Java集合框架中。当在迭代集合的过程中该集合在结构上发生改变如添加、删除元素时快速失败机制会立即抛出ConcurrentModificationException异常。这种机制的设计目的是提高软件的可靠性和稳定性避免在出现问题时让问题持续扩散并造成更严重的后果。
安全失败机制通过在遍历之前将集合“原件”进行“复印”使得在遍历过程中对其他线程对原集合的修改不会影响遍历操作。换句话说当其他线程想要修改集合数据时修改的是“复印件”而不是原集合数据。这样即使原集合在遍历过程中被修改也不会抛出异常或导致数据不一致。在Java中CopyOnWriteArrayList就是一个提供安全失败机制的集合类。
原因迭代器在遍历时直接访问集合中的内容并且在遍历的过程中使用一个modCount变量。集合在被遍历期间如果内容发生变化就会改变modCount的值。每当迭代器使用hashNext、naxt遍历下一个元素前都会检测modcount变量是否为expectedmodCount值是的话就返回遍历否则就抛出异常终止遍历。
解决办法
在遍历过程中所有涉及到改变modCount值得地方都加上synchronized使用CopyOnWriteArrayList来代替arrayList; CopyOnWriteArrayList采用的是读写分离写时先复制然后指向新地址 20.怎么确保一个集合不能被修改
21.Iterator和ListIterator区别
Iterator可以遍历Set和List集合ListIterator只能遍历ListIterator只能单向遍历ListIterator双向遍历(向前/后遍历)ListIterator实现Iterator接口然后添加了一些额外功能比如添加、替换等。
22.移除collection元素
边遍历变修改Collection的唯一正确的方式是使用Iterator.remove().
23.遍历list的不同方式
使用for循环遍历使用for-each循环增强型for循环使用Iterator接口使用Java 8的Stream API
24.ArrayList为什么不直接序列化其内部数组
灵活性如果 ArrayList 直接序列化其内部的数组那么每次修改 ArrayList 的内部结构例如通过添加或删除元素时都需要重新序列化整个数组。这会导致不必要的开销因为只有一部分数据实际上发生了变化。通过将 ArrayList 的元素逐个序列化可以避免这种开销。可扩展性ArrayList 的一个主要特点是它可以动态地增长和缩小。如果 ArrayList 直接序列化其内部的数组那么当数组需要增长时就需要创建一个新的、更大的数组并将旧数组的内容复制到新数组中。这会增加序列化和反序列化的复杂性。通过逐个序列化元素可以避免这种复杂性。安全性直接序列化内部数组可能会暴露 ArrayList 的内部结构这可能会导致安全问题。例如攻击者可能会利用这个信息来攻击 ArrayList 的实现。通过逐个序列化元素可以更好地控制哪些信息被暴露。 总的来说通过逐个序列化元素而不是直接序列化内部数组ArrayList 可以提供更好的性能、可扩展性和安全性。 25.快排时间复杂度
快速排序的时间复杂度为O(n log n)
26.什么叫序列化
序列化Serialization是将对象的状态信息转换为可以存储或传输的字节流的形式的过程。在序列化期间对象将其当前状态写入到临时或持久性存储区。以后可以通过从存储区中读取或反序列化对象的状态重新创建该对象。
27.和equals的区别
如果你关心的是两个对象是否指向同一个内存地址那么应该使用如果你关心的是两个对象的内容是否相同那么应该使用equals
28.14是什么意思
14 是一个位运算表达式表示将数字 1 的二进制表示向左移动 4 位。
首先数字 1 的二进制表示是 0000 0001假设我们在这里使用8位二进制数来表示但实际上使用的位数可能取决于具体的系统和编程语言的实现。
当我们执行 14 时这个二进制数向左移动4位变为 0001 0000。
在十进制中0001 0000 等于 16。
因此14 的结果是 16。
29.时间复杂度的排序
O(1)O(logn)O(n)O(n^2)
30.HashMap中string、Integer这样的包装类适合作为key么任何类都可以作为key么Object作为HashMap的Key应该怎么做
包装类的特性能够保证Hash值得不可更改性和计算准确性能够减少Hash碰撞的几率可以使用任何类作为Map的key需要重写hashCode和equals方
31.Collection和Collections的区别
Collection是一个集合接口主要提供了对集合对象进行基本操作的通过接口方法。 Collections是针对于集合类的一个包装类主要提供了一系列的静态方法以实现对各种集合的搜索、排序、线程安全化操作
32.Collections.sort的原理
Collections.sort是Java中的一个静态方法用于对List类型的集合进行排序。其排序原理依赖于所使用的排序算法具体取决于待排序集合中元素的类型。
对于基本数据类型如int, short, long等Collections.sort通常使用快速排序算法。快速排序是一种分治策略的排序算法通过选择一个“基准”元素将集合分为两部分一部分小于基准一部分大于基准然后递归地对这两部分进行排序。对于Object类型Collections.sort通常使用归并排序算法。归并排序也是一种分治策略的排序算法它将集合不断地分为两半分别进行排序然后将排序好的两个集合合并成一个。归并排序是稳定的即相等的元素在排序后保持原有的顺序。
在选择排序算法时Collections.sort也考虑到了性能因素。对于小规模的集合通常是小于或等于60个元素Collections.sort可能会使用插入排序算法因为插入排序在处理小规模数据时通常比快速排序和归并排序更快。
需要注意的是Collections.sort方法通过调用Arrays.sort方法来实际执行排序操作因为Collections类中的许多方法都是基于Arrays类中的方法实现的。Arrays.sort方法对于Object类型的数组如果数组元素实现了Comparable接口则使用归并排序否则如果提供了Comparator对象则使用Timsort算法。
33.怎么确保一个集合不被修改
可以使用Collections.unmodifiableCollection(Collection c)方法来创建一个只读集合这样改变集合的任何操作都会抛出Java.lang.UnsupportedOperationExection异常。
34.哪些集合类是线程安全的
Java 集合框架中线程安全的集合类包括
VectorVector 是线程安全的动态数组类与 ArrayList 类似但它是同步的。StackStack 类是 Vector 的一个子类它实现了一个后进先出的堆栈。HashtableHashtable 是一个散列表和 HashMap 类似但是它是同步的可以用来在多个线程之间共享键值对。ConcurrentHashMapConcurrentHashMap 是一个线程安全的散列表它可以在多个线程之间同时使用而不需要任何额外的同步工具。ConcurrentLinkedQueueConcurrentLinkedQueue 是一个线程安全的队列它是非阻塞的并且能够保证在多线程并发访问时元素顺序的正确性。
此外Java 并发包java.util.concurrent下的其他集合类如 CopyOnWriteArrayList、CopyOnWriteArraySet、BlockingQueue 接口的实现类如 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue 等也是线程安全的。