面试小结(三)

数据结构

        数据结构是计算机存储、组织数据的方式,是算法的基础和核心。它能够帮助我们更加高效地处理数据,提高程序的执行效率,减少内存的占用,从而使得程序更加稳定、可靠、可扩展。数据结构可以分为线性结构、树结构、图结构等等,每种结构都有其独特的特点和适用场景;

分析一下JAVA中LinkedList数据结构

        LinkedList是Java中的双向链表数据结构,它实现了List接口,同时也实现了Queue和Deque接口,可以作为队列、栈、双向队列使用。相对于ArrayList,LinkedList的特点是插入、删除元素的效率较高,但是查询和遍历的效率较低!

        在LinkedList中,每个元素都是一个节点,节点包含了当前元素的值以及指向前驱点和后继节点的引用。LinkedList的头节点不包含任何实际的元素,仅仅作为链表的起始点;

LinkedList的基本操作如下:

1.在链表的末尾添加元素,时间复杂度O(1);

2.在链表的头部添加元素,时间复杂度O(1);

3.在链表指定位置添加元素,需要遍历链表找到该位置,时间复杂度为O(n);

4.删除链表中的指定元素,需要遍历链表找到该元素,时间复杂度为为O(n);

5.删除链表中头节点或者尾节点,时间复杂度为O(1);

6.获取链表中指定位置的元素,需要遍历链表找到该位置,时间复杂度为O(1);

线程安全性:在多线程环境下,LinkedList并不是线程安全的,需要进行同步控制,同时频繁的插入,删除操作会影响性能;

分析一下Java中的ArrayList数据结构

        ArrayList是Java中的动态数据存储结构,它实现了List接口,可以通过下标访问元素,支持动态扩容和缩容,可以存储任意类型的对象;

        在ArrayList中,元素被存储在一个连续的内存空间中,每个元素可以通过下标访问,下标从0开始,ArrayList有一个默认初始容量为10的数组,当元素数量达到数组容量时,会自动扩容为原来的1.5倍。当元素数量小于数组容量的一半时,会自动缩容为原来的一半;

ArrayList支持以下基本操作:

1.在数组末尾添加元素,时间复杂度为O(1);

2.在数组指定位置添加元素,需要将插入位置之后的元素依次后移一位,时间复杂度为O(n);

3.在数组中删除指定元素,需要将插入位置之后的元素依次前移一位,时间复杂度为O(n);

4.删除数组的头元素或者尾元素,时间复杂度为O(1);

5.获取数组中指定位置的元素,时间复杂度为O(1);

6.遍历数组,将数组元素依次取出进行操作,时间复杂度为O(n);

线程安全性:在多线程环境下,ArrayList并不是线程安全的,需要进行同步控制,同时,在使用 ArrayList 时,需要注意数组的扩容和缩容会带来一定的性能开销,尽量减少数组的频繁扩容和缩容操作;

分析一下Java中的HashMap数据结构

        HashMap是Java中常用的一种数据结构,主要是实现的Map接口,可以将键(Key)映射到值(Value)上。下面是对HashMap的简单分析:

        原理:HashMap底层采用的是数组+链表/红黑树实现,用数组存储元素,通过key的hashCode()方法得到元素的存储位置,可能会存在哈希冲突,当哈希冲突时,会采用链表或者红黑树等数据结构来存储值;

        哈希冲突:由于哈希算法不可避免的存在哈希冲突,因此HashMap在插入元素时需要进行哈希冲突的处理,可以通过链表,红黑树等数据结构来存储值。在JDK8中,当链表长度达到8时,链表转换成红黑树,当红黑树节点数量少于6时,红黑树退化成链表;

        扩容机制:当HashMap中元素个数超过负载因子(默认为0.75)时,会自动进行扩容操作,将容量翻倍,同时将所有元素重新hash并存储到新的数组中;

        并发性:HashMap在多线程环境下存在线程安全问题,因为多个线程可能同时对同一个哈希桶进行操作,可能会导致链表成环等问题。在 JDK8 中,HashMap 采用了 CAS 和 synchronized 等机制来保证线程安全;

        性能优化:为了提高HashMap的性能,可以通过指定初始容量,负载因子,使用LinkedHashMap等方式进行优化。同时为了避免哈希冲突,可以通过优化哈希函数,使用更好的哈希算法来进行优化;

分析一下Java中的CurrentHashMap数据结构

ConcurrentHashMap 是 Java 中的一个线程安全的哈希表,它是 HashMap 的线程安全版本。ConcurrentHashMap 在并发环境下提供高效的线程安全操作,同时也保持了较低的锁竞争,因此适用于多线程并发操作。

下面是 ConcurrentHashMap 的主要特点和数据结构:

  1. 分段锁:ConcurrentHashMap 内部采用分段锁(Segment)的机制,它将整个哈希表分为多个小的片段,每个片段拥有自己的锁。这样在多线程情况下,不同的线程可以同时访问不同的片段,减少了锁竞争,提高了并发性能。

  2. 支持高并发读操作:多个线程可以同时进行读操作,不会阻塞彼此,因为读操作只会锁住对应的分段,而不是整个哈希表。

  3. 适用于读多写少的场景:ConcurrentHashMap 的性能在读多写少的场景中表现出色。对于写操作,仍然需要获得相应分段的锁,因此写操作需要注意竞争情况。

  4. 安全删除操作:ConcurrentHashMap 允许在迭代的同时进行删除操作,不会抛出 ConcurrentModificationException 异常。

  5. 线程安全的操作:除了支持并发读操作,它也支持多线程并发写操作,这使得它在高并发环境下非常有用。

  6. 可调整大小:ConcurrentHashMap 可以在初始化时指定初始容量和负载因子,如果哈希表的负载因子超过了指定值,它会进行扩容

  • JDK 7 中的 ConcurrentHashMap:JDK 7 中的 ConcurrentHashMap 使用了分段锁来提供并发性。虽然它在很多情况下表现出色,但在高度竞争的情况下仍可能出现性能问题。在 JDK 7 中,ConcurrentHashMap 会导致一定程度的锁竞争。

  • JDK 8 中的 ConcurrentHashMap:在 JDK 8 中,ConcurrentHashMap 的内部结构发生了改变,采用了更现代的 CAS 操作和节点数组来提供更好的并发性能。JDK 8 中的 ConcurrentHashMap 引入了分离锁和链表替代结构,降低了锁的竞争,并在大多数情况下提供了更好的性能。ConcurrentHashMap 使用了 Node 数组代替了 JDK 7 中的分段锁,从而提高了并发性能。

  • JDK 9+ 中的 ConcurrentHashMap:在 JDK 9+ 中,ConcurrentHashMap 的数据结构和性能继续改进。特别是在 JDK 16 中,引入了更多的性能优化,例如改进的哈希表性能和更好的并发性能。

52.HashMap类型put和get的底层实现?

在 HashMap 中,put() 方法会将键值对存储到哈希表中。具体的实现步骤如下:

首先,根据键的 hashCode() 方法计算出一个哈希码(即对应的数组下标)。

如果该位置没有被占用,则直接将键值对存储在该位置上,并返回 null。

如果该位置已经被占用,则需要检查该位置上的键是否与要插入的键相同。如果相同,则更新该位置上的值;否则,使用开放寻址法或链式存储法解决哈希冲突。

如果当前 HashMap 的 size 大于等于负载因子(默认为 0.75),则需要将哈希表扩容。扩容后,原来的键值对需要重新哈希并放置到新的位置上。

get() 方法会根据指定的键返回对应的值。具体的实现步骤如下:

根据键的 hashCode() 方法计算出哈希码。

在哈希表中查找该哈希码对应的位置。如果该位置为空,则返回 null。

如果该位置不为空,则需要根据键的 equals() 方法检查该位置上的键是否与要查找的键相同。如果相同,则返回该位置上的值;否则,使用开放寻址法或链式存储法在哈希表中继续查找。

总之,在 HashMap 中,put() 方法通过将键值对映射到哈希表中的一个索引位置来实现插入操作,而 get() 方法则通过根据键的哈希码和 equals() 方法来查找对应的值。同时,为了解决哈希冲突,HashMap 可以使用开放寻址法或链式存储法等技术进行优化

什么是哈希冲突?哈希冲突解决方法?

哈希冲突(Hash Collision)指的是当两个或多个不同的输入值经过哈希函数运算后,得到相同的哈希值。哈希冲突是不可避免的,因为哈希函数的输出空间通常远小于输入空间,这就意味着有限数量的哈希值必然要映射到同一个哈希桶中。

解决哈希冲突的方法通常有以下几种:

  1. 链地址法(Chaining):这是最常见的方法。在每个哈希桶中,维护一个链表或其他数据结构,用于存储所有哈希到该桶的键-值对。当发生冲突时,新的键-值对会被添加到该桶对应的链表中。这种方法适用于高负载因子的情况,即哈希表中的键-值对数量很多。

  2. 线性探测法(Linear Probing):当发生哈希冲突时,线性探测法会检查下一个哈希桶,直到找到一个空的桶为止。这个方法适用于低负载因子的情况,即哈希表中的键-值对数量相对较少。

  3. 二次探测法(Quadratic Probing):和线性探测法类似,但它使用二次函数来寻找下一个哈希桶。这种方法在一些情况下能够更均匀地分散冲突,但仍然适用于低负载因子。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

网友小浩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值