Java基础面试题精选:深入探讨哈希表、链表和接口等

news2024/10/6 18:34:06

目录

1.ArrayList和LinkedList有什么区别?🔒

2.ArrayList和Vector有什么区别?🔒

3.抽象类和普通类有什么区别?🔒

4.抽象类和接口有什么区别?🔒

5.HashMap和Hashtable有什么区别?🔒

6.HashMap底层是如何实现的?🔒

7.HashMap和HashSet有什么区别?🔒

8.说说你对红黑树的见解?🔒

9.哈希冲突及解决方法?🔒

10.HashMap 和 ConcurrentHashMap 的区别🔒

11.ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?🔒

12.针对ConcurrentHashMap锁机制具体分析(JDK1.7VSJDK1.8)?🔒

13. ConcurrentHashMap在JDK1.8中,为什么要使用内置锁synchronized来代替重入锁ReentrantLock?🔒

14.什么是负载因子,为什么是0.75🔒


1.ArrayList和LinkedList有什么区别?🔒

ArrayList和LinkedList都是List接口的实现类~~

🔑🔑🔑它们有以下几个方面不同:

(1)它们的底层实现不同:ArrayList 是基于动态数组的数据结构,而 LinkedList 是基于链表的数据结构。 

(2)随机访问性能不同ArrayList 优于 LinkedList,因为 ArrayList 可以根据下标以 O(1) 时间复杂度对元素进行随机访问。而 LinkedList 的访问时间复杂度为 O(n),因为它需要遍历整个链表才能找到指定的元素。

🔨ArrayList的内部实现是基于基础的对象数组的,因此,它使用get方法访问列表中的任意一个元素时,它的速度要比LinkedList快。

🔨不同的是LinkedList中的get方法是按照顺序从列表的一端开始检查,直到另外一端。

(3)插入和删除性能不同LinkedList 优于 ArrayList,因为 LinkedList 的插入和删除操作时间复杂度为 O(1),而 ArrayList 的时间复杂度为 O(n)。

🔨当一个元素被加到ArrayList的最开端时,所有已经存在的元素都会后移,这就意味着数据移动和复制上的开销。

🔨相反的,将一个元素加到LinkedList的最开端只是简单的为这个元素分配一个记录,然后调整两个连接。

2.ArrayList和Vector有什么区别?🔒

 ArrayList和Vector都是List接口的实现类,它们都是动态数组的实现,具有相同的方法(增删改查)

🔑🔑🔑它们有以下几个方面不同:

(1)线程安全性Vector是线程安全的,而ArrayList不是。所以在多线程的环境下,应该选择Vector。

🔨在多线程环境下,多个线程同时对 ArrayList 进行添加元素的操作,可能会导致数据错乱、数组越界等问题。

🔨Vector大部分方法都使用了synchronized关键字来修饰,这使得在访问Vector对象时同一时间只能有一个线程执行操作,其他线程必须等待。这样可以避免多线程同时修改Vector对象而引发的数据竞争和不一致性问题。

(2)性能:由于Vector是线程安全的,所以它的性能比ArrayList差,在单线程的情况下,ArrayList的速度比Vector快。

(3)初始容量增长方式:当容量不足时,ArrayList会增加50%的容量,而Vector会将容量翻倍,在添加大量元素时,ArrayList需要频繁的扩容操作,所以Vector更适合存储大量数据

3.抽象类和普通类有什么区别?🔒

在Java中,抽象类和普通类是两种不同的类类型,

🔑🔑🔑以下是普通类和抽象类的一些区别: 

(1)实例化普通类可以直接实例化,而抽象类不能直接实例化。

🔨抽象类通常用于定义一些基本的行为和属性,而具体的实现则由其子类来完成。

(2)方法抽象类中既包含抽象方法又可以包含具体的方法,而普通类只能包含普通方法。

(3)实现普通类实现接口需要重写接口中的方法,而抽象类可以实现接口方法也可以不实现。

4.抽象类和接口有什么区别?🔒

在Java中,抽象类和接口是两种不同的类类型,它们都不能直接实例化,它们通常用来定义一些基本的属性和方法~~

🔑🔑🔑它们有以下几个方面不同:

(1)定义:定义的关键字不同,抽象类是 abstract,而接口是 interface。

(2)方法:抽象类可以包含抽象方法和具体方法,而接口只能包含方法声明(抽象方法)

(3)方法访问控制符:抽象类无限制,只是抽象类中的抽象方法不能被 static、final、private 修饰;而接口有限制,接口默认的是 public 控制符

🔨抽象方法不能被private修饰是因为:private修饰的方法或变量只能在本类中访问、调用,而抽象方法是必然要被子类覆盖重写的。
🔨抽象方法不能被static修饰是因为:static修饰的方法是属于类的,首先要满足类的调用,如果通过类无法调用,那么这个静态方法就肯定不正确了,为了满足这个要求就必须有方法体,有方法体就不能是抽象方法。且static修饰的方法不能被重写,也就是说这个抽象类的子类不能重写这个方法与抽象方法的本质相驳。
🔨抽象方法不能被final修饰是因为:被final修饰的方法无法被子类重写,而抽象方法被非抽象类继承就必须要重写所有抽象方法,所以抽象方法不能被final修饰。

🔨接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。

(4)实现:一个类只能继承一个抽象类,但可以实现多个接口

(5)变量:抽象类可以包含实例变量和静态变量,而接口只能包含常量

接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量,即常量(并且只能是 public,用 private 修饰会报编译错误)。 

(6)构造函数:抽象类可以有构造函数,而接口不能有构造函数

🔨抽象类是一个类,别的类是用关键字 extends 来继承下来,并扩展的,有非常强的is-a的关系,

🔨接口只是定义功能和行为规范,如果一个类实现了一个接口,那么这个类必须遵守这个接口的方法约定,但没有is-a的关系。把墙壁上的“小学生行为规范”想象成一个接口,那么是小学生必须遵守这个约定,但小学生不是“行为规范”。接口是一种规范,被调用时,主要关注的是里边的方法,而方法是不需要初始化的,类可以实现多个接口

5.HashMap和Hashtable有什么区别?🔒

HashMap和Hashtable都实现了Map接口,都是Java中用于存储键值对的数据结构,它们的底层数据结构都是数组加链表的形式(默认情况下),

🔑🔑🔑但它们存在以下几点不同:

(1)线程安全:Hashtable 是线程安全的,而 HashMap 是非线程安全的。

1.在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。

2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。

(2)性能:因为 Hashtable 使用了 synchronized 给整个方法添加了锁,所以相比于 HashMap 来说,它的性能不如 HashMap

(3)存储:HashMap 允许 key 和 value 为 null,而 Hashtable 不允许存储 null 键和 null 值

 参照Hashtable源码:

🔨Hashtable 不能存储 null 键和 null 值是因为,它的 key 值要进行哈希计算,如果为 null 的话,无法调用该方法,还是会抛出空指针异常。而 value 值为 null 的话,Hashtable 源码中会主动抛出空指针异常。

参照HashMap源码:

🔨HashMap 允许 key 和 value 为 null 的原因是因为在 HashMap 中对 null 值进行了特殊处理,如果为 null 时会把它赋值为 0,

6.HashMap底层是如何实现的?🔒

HashMap 在不同的 JDK 版本下的实现是不同的,在 JDK 1.7 时,HashMap 底层是通过数组 + 链表实现的;而在 JDK 1.8 时,HashMap 底层是通过数组 + 链表或红黑树实现的。

(1)数组存储桶:HashMap内部维护一个数组,这个数组的每个元素称为桶(bucket),每个桶可以存储一个链表或红黑树。这个数组的大小是可以调整的,它的初始大小是16,但可以根据需要自动扩展。

(2)哈希函数:当你将一个键值对放入HashMap时,HashMap会使用键的哈希码(通过调用键的hashCode()方法获得)来确定这个键应该存储在数组的哪个桶中。哈希函数的目标是将键均匀地分布在不同的桶中,以便高效地检索数据。

(3)碰撞处理:由于哈希函数的限制,不同的键可能会映射到相同的桶中,这就产生了碰撞(collision)。为了解决碰撞问题,每个桶都可以存储一个链表或红黑树。如果多个键哈希到同一个桶,它们将被存储在同一个链表或红黑树中。在Java 8及之后的版本中,当链表中的元素数量超过一定阈值时,链表将被转换为红黑树,以提高检索效率。

🔨链表来存储相同桶内的键值对的示意图:

HashMap:
  0  -> []
  1  -> []
  2  -> [ (Key1, Value1), (Key2, Value2) ]
  3  -> []
  4  -> []
  5  -> []
  6  -> []
  7  -> []
HashMap:
  0  -> []
  1  -> []
  2  -> [ (Key1, Value1) -> (Key2, Value2) ]
  3  -> []
  4  -> []
  5  -> []
  6  -> []
  7  -> []

🔨红黑树来存储相同桶内的键值对的示意图:

HashMap:
  0  -> []
  1  -> []
  2  -> [ (Key1, Value1), (Key2, Value2), (Key3, Value3), ... ]
  3  -> []
  4  -> []
  5  -> []
  6  -> []
  7  -> []
HashMap:
  0  -> []
  1  -> []
  2  -> [ (Key1, Value1)
           /  \
  (Key2, Value2)  (Key3, Value3)
                    \
                 (Key4, Value4) ]
  3  -> []
  4  -> []
  5  -> []
  6  -> []
  7  -> []

(4)查找和插入:当你想要查找HashMap中的值时,首先计算键的哈希码,然后根据哈希码找到对应的桶,接着在桶中查找键对应的值。插入操作也类似,只是要在桶中插入键值对。

(5)自动扩容:当HashMap中的元素数量超过了数组容量的一定比例(负载因子,默认为0.75),HashMap会自动扩容,即创建一个更大的数组,并将所有的键值对重新分配到新数组中,以保持哈希表的效率。

(6)删除:在Java的HashMap实现中,当红黑树的节点数量减少到小于等于6时,会将红黑树重新转换为链表结构。这是为了避免过于复杂的数据结构,因为红黑树相对于链表来说,维护和查找节点的开销更大。这种转换是为了维护HashMap的性能和内存使用效率。

HashMap:
  0  -> []
  1  -> []
  2  -> [ (Key1, Value1)
           /  \
  (Key2, Value2)  (Key3, Value3) ]
  3  -> []
  4  -> []
  5  -> []
  6  -> []
  7  -> []
HashMap:
  0  -> []
  1  -> []
  2  -> [ (Key1, Value1) -> (Key2, Value2) -> (Key3, Value3) ]
  3  -> []
  4  -> []
  5  -> []
  6  -> []
  7  -> []

7.HashMap和HashSet有什么区别?🔒

HashMap 和 HashSet 都是 Java 中的集合类,

🔑🔑🔑但它们存在以下几点不同:

  1. 数据结构

    • HashMap:是基于键值对(key-value pair)的数据结构,用于存储一组键值对,并根据键来查找值。HashMap内部使用哈希表来实现,每个键都映射到唯一的值。它允许键和值都可以为null,并且支持键的重复(但不同键的哈希码不能相同)。
    • HashSet:是一种基于哈希表的集合,用于存储一组唯一的元素,不允许重复。它内部实际上使用了HashMap,但HashSet只关注集合中的元素,而不存储键值对。HashSet允许元素为null,但不允许重复元素。
  2. 用途

    • HashMap:常用于需要按键查找值的情况,例如,存储和检索关联性数据,快速查找等。它提供了更多的灵活性,可以存储任何对象类型作为键和值。
    • HashSet:常用于需要存储唯一元素的情况,例如,去重操作、检查元素是否存在等。HashSet通常用于存储集合中的元素,而不需要与特定键相关联。
  3. 操作

    • HashMap:提供了put、get、remove等操作,通过键来操作值。
    • HashSet:提供了add、remove、contains等操作,通过元素来操作集合。
  4. 性能

    • HashMap:通常用于需要高效查找的场景,平均情况下,查找、插入和删除操作的时间复杂度接近O(1)。
    • HashSet:也用于高效查找,但HashSet主要关注元素的存在性,因此在去重或检查元素是否存在的操作上更为适用。

8.说说你对红黑树的见解?🔒

🔑🔑🔑

自平衡性:红黑树是一种自平衡树,它通过在每个节点上引入红色或黑色标记,并通过一组规则来确保树的平衡。这些规则确保了树的高度保持在对数级别,从而保证了基本操作(如查找、插入和删除)的时间复杂度为O(log n),其中n是树中节点的数量。

节点颜色:每个节点都被标记为红色或黑色。这些颜色规则的主要目的是确保以下性质:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色的。
  3. 每个叶子节点(NIL节点)都是黑色的。
  4. 如果一个节点是红色的,则其两个子节点必须是黑色的。
  5. 从任意节点到其每个叶子节点的简单路径都包含相同数量的黑色节点,这被称为“黑高度相等”。

示意图:

         7 (黑)
        / \
   3 (红)  18 (黑)
  / \    /  \
1(黑)  5(黑)  23(红)
          /   / \
       15(黑)  28(黑)

插入操作:在插入新节点时,需要遵循一组规则来保持树的平衡。通常,插入操作会导致颜色变化和树的旋转操作,以确保树的红黑树性质不被破坏。

现在,我们要插入一个新的节点,例如插入值为10的节点:

插入10:
         7 (黑)
        / \
   3 (红)  18 (黑)
  / \    /  \
1(黑)  5(黑)  23(红)
         \
         10 (红)

在插入节点10后,由于节点3和节点10都是红色,违反了红黑树的性质。为了修复这个问题,我们需要进行颜色变化和旋转操作:

颜色变化:
         7 (红)
        / \
   3 (黑)  18 (黑)
  / \    /  \
1(黑)  5(黑)  23(红)
         \
         10 (黑)

将节点3和节点10的颜色由红色变为黑色,同时将节点7的颜色由黑色变为红色。这确保了从根节点到叶子节点的每个路径上的黑高度保持不变。

左旋转:
         7 (红)
        / \
   5 (黑)  18 (黑)
  / \    /  \
3(黑)  10(黑)  23(红)
  \
  1 (黑)

由于插入节点10后,节点3和节点10形成了连续的红色节点,违反了红黑树的性质。为了修复这个问题,我们执行左旋转操作

完成这些操作后,红黑树再次满足所有红黑树性质,插入操作成功完成。

删除操作:删除节点时也需要进行颜色变化和旋转操作来维护红黑树的性质。删除操作可能比插入操作更复杂,因为需要处理不同情况下的平衡问题。

性能:红黑树的平衡性保证了查找、插入和删除操作的平均时间复杂度为O(log n)。这使得红黑树非常适合需要高效的搜索和更新操作的情况,例如,在各种编程语言和库中广泛用于实现字典、集合和映射数据结构。

应用:红黑树在计算机科学中有广泛的应用,包括在操作系统中管理文件系统的数据结构、在数据库中维护索引、在编程语言的集合库中实现集合和映射等。

9.哈希冲突及解决方法?🔒

哈希冲突是指两个或多个不同的输入值(通常是键)经过哈希函数处理后得到相同的哈希值。哈希函数的目标是将不同的输入均匀分布到哈希表的不同位置,但由于输入值的多样性远远超过了哈希表的大小,所以在实际使用中,哈希冲突是不可避免的。

🔑🔑🔑 

哈希冲突可能会导致以下问题:

  1. 数据丢失:当不同的键映射到相同的哈希桶(数组位置)时,只能存储一个键值对,因此可能会丢失某些数据。

  2. 性能下降:哈希冲突会导致多个键值对存储在同一个哈希桶中,这会增加在该桶中查找特定键值对的时间复杂度,因为需要在链表或其他数据结构中进行线性搜索。

哈希表:
0: [1, 6]
1: [2]
2: [3]
3: [4]
4: [5]

在这个示意图中,我们插入了一组数字键,每个键都经过哈希函数后映射到相应的桶中。然而,由于哈希函数的限制以及桶的数量有限,冲突发生了。例如,键1和键6都被映射到桶0中,键2、3、4和5都被映射到不同的桶中。

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

  1. 开放寻址法(Open Addressing):在发生冲突时,该方法会尝试找到哈希表中的下一个可用位置,并将键值对存储在那里,直到找到一个空位置或达到一定的尝试次数。

  2. 链地址法(Chaining):在这种方法中,每个哈希桶存储一个链表或其他数据结构,用于存储相同哈希值的多个键值对。当冲突发生时,新的键值对被添加到链表的末尾。

  3. 再哈希(Rehashing):当哈希表的负载因子(已存储键值对的数量与哈希桶的数量的比率)达到一定阈值时,可以进行再哈希操作,即创建一个更大的哈希表,并将所有键值对重新散列到新的表中,以减少冲突的发生。

  4. 使用更好的哈希函数:选择一个更好的哈希函数可以减少哈希冲突的发生。好的哈希函数能够均匀地将输入映射到不同的哈希值,从而减少冲突的可能性。

 下面演示一下链地址法:

桶 = 哈希值 % 表的长度

插入键值对 (3, "A"):
哈希表:
0: []
1: []
2: []
3: ["A"]
4: []

插入键值对 (7, "B"):
哈希表:
0: []
1: []
2: []
3: ["A"]
4: ["B"]

插入键值对 (12, "C"):
哈希表:
0: []
1: []
2: []
3: ["A"]
4: ["B", "C"]

插入键值对 (17, "D"):
哈希表:
0: []
1: []
2: ["D"]
3: ["A"]
4: ["B", "C"]

插入键值对 (22, "E"):
哈希表:
0: []
1: []
2: ["D"]
3: ["A"]
4: ["B", "C", "E"]

在这个示意图中,我们可以看到哈希冲突的情况发生在键12和键22,它们都经过哈希函数后映射到了桶2中。为解决冲突,我们使用了链地址法,将具有相同哈希值的键值对存储在同一个桶中的链表中。

10.HashMap 和 ConcurrentHashMap 的区别🔒

HashMapConcurrentHashMap 都是 Java 中用于存储键值对的数据结构,但它们之间有一些重要的区别,特别是在多线程环境下的行为。

🔑🔑🔑 以下是它们之间的主要区别:

  1. 线程安全性:

    • HashMapHashMap 不是线程安全的,这意味着如果多个线程同时访问一个 HashMap 实例并且至少有一个线程在进行写操作(插入、删除等),则需要外部同步来确保线程安全。
    • ConcurrentHashMapConcurrentHashMap 是线程安全的。它使用了一种分段锁的机制,将数据分成多个段(segments),每个段拥有自己的锁。这使得多个线程可以并发地访问不同的段,从而提高了并发性能。
  2. 性能:

    • 高度并发的情况下,ConcurrentHashMap 的性能通常比HashMap好,因为多个线程可以并行地读取和写入不同的段,而不会彼此阻塞。
    • HashMap 在单线程环境中的性能可能会稍微好一些,因为不需要额外的同步开销。
  3. Null 键和值:

    • HashMap 允许使用null作为键和值。
    • ConcurrentHashMap 不允许使用null作为键或值。如果试图插入null键或值,将会抛出NullPointerException
  4. 迭代器:

    • HashMap迭代器不是线程安全的。如果在迭代期间修改 HashMap,可能会导致 ConcurrentModificationException 异常。
    • ConcurrentHashMap迭代器是弱一致性的,允许在迭代期间进行修改,但不保证迭代器会反映出所有修改。

ConcurrentHashMap 不允许使用 null 作为键或值的设计是为了确保在并发环境下的一致性和可靠性。允许 null 作为键或值可能导致以下问题:

  1. 空指针异常(NullPointerException):如果允许 null 作为键或值,那么在获取或操作映射中的数据时,就会产生潜在的 NullPointerException 风险。这会使代码更难以编写和维护,因为您需要不断地检查键和值是否为 null,以避免异常。

  2. 一致性问题:在多线程环境下,如果一个线程在映射中插入了 null 键或值,而另一个线程在尝试读取或修改这些 null 数据,可能会导致不一致的行为或结果。这会破坏了映射的一致性和可预测性。

  3. 散列冲突问题ConcurrentHashMap 使用散列函数将键映射到桶(buckets)上,如果允许 null 键,那么计算 null 键的散列值可能会引发异常或者导致不正确的数据分布,因为 null 没有散列值。这可能会导致映射无法正常工作。

总的来说,如果需要在多线程环境中安全地操作一个映射(键值对集合),则应该使用 ConcurrentHashMap。如果只在单线程中使用或者可以手动同步的情况下,可以考虑使用 HashMap。需要注意的是,在 Java 8 之后,ConcurrentHashMap 进行了一些改进,性能更好,因此在大多数情况下,它是更好的选择。

11.ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?🔒

🔑🔑🔑 JDK 1.8不同于 JDK 1.7 版本的 Segment 数组 + HashEntry 链表,JDK 1.8 版本中的 ConcurrentHashMap 直接抛弃了 Segment 锁,一个 ConcurrentHashMap 包含一个 Node 数组(和 HashEntry 实现差不多),每个 Node 是一个链表结构,并且在链表长度大于一定值时会转换为红黑树结构(TreeBin)。

既然没有使用分段锁,如何保证并发安全性的呢?

synchronized + CAS!

简单来说,Node 数组其实就是一个哈希桶数组,每个 Node 头节点及其所有的 next 节点组成的链表就是一个桶,只要锁住这个桶的头结点,就不会影响其他哈希桶数组元素的读写。桶级别的粒度显然比 1.7 版本的 Segment 段要细。

🔨ConcurrentHashMap 使用了 synchronized 来确保多个线程可以安全地访问集合。这意味着对 ConcurrentHashMap 的操作在方法级别或块级别上是同步的,以防止多个线程同时修改集合,从而确保线程安全。

🔨CAS 策略来实现并发性,允许多个线程同时读取和修改不同部分的哈希表,而不会阻塞整个表。这种方式提高了并发性能,因为只有在发生冲突时才需要同步。CAS 在这里用于更新桶(buckets)内的数据,从而实现更细粒度的并发。

12.针对ConcurrentHashMap锁机制具体分析(JDK1.7VSJDK1.8)?🔒

🔑🔑🔑

JDK 1.7 中的 ConcurrentHashMap:

  1. Segment-Based设计:

    • JDK 1.7 中的 ConcurrentHashMap 使用了分段锁的设计。它将整个数据结构分为多个段(Segment),每个段维护自己的哈希表。
    • 每个段都有一个独立的锁ReentrantLock),因此不同的段之间的操作可以并行执行,从而提高了并发性能。
  2. 锁粒度:

    • 读操作和写操作都需要获取特定段的锁。这种设计允许多个线程同时进行读操作,但写操作需要获取段级别的锁,因此在高度并发写入的情况下可能会导致性能瓶颈。
  3. 并发度:

    • JDK 1.7 的 ConcurrentHashMap 可以支持多个读操作并发执行,但写操作需要获取段级别的锁,这可能限制了写操作的并发度。

JDK 1.8 中的 ConcurrentHashMap:

  1. CAS-Based设计:

    • JDK 1.8 中的 ConcurrentHashMap 引入了基于 CAS(Compare-And-Swap)的设计,摒弃了分段锁。
    • 内部数据结构将哈希表分成多个桶(buckets),每个桶下面是一个链表或红黑树。
  2. 锁粒度:

    • JDK 1.8 中的 ConcurrentHashMap 不再使用段级别的锁。读操作和写操作都不需要全局锁或段级别的锁。
    • 写操作使用 CAS 操作和其他无锁算法来实现线程安全,降低了锁的竞争。
  3. 并发度:

    • JDK 1.8 的 ConcurrentHashMap 在设计上提高了并发度,因为读操作和写操作不再需要锁定整个数据结构或段,而是使用了无锁技术。
  4. 红黑树优化:

    • 当链表长度达到一定阈值时,JDK 1.8 的 ConcurrentHashMap 会将链表转化为红黑树,以提高查找性能。这个优化有助于防止在极端情况下链表过长而导致的性能下降。

总结:

  • JDK 1.7 中的 ConcurrentHashMap 使用了分段锁来实现并发性,适用于一定程度的并发,但在高度并发写入场景下可能有性能限制。
  • JDK 1.8 中的 ConcurrentHashMap 引入了更先进的无锁技术,消除了分段锁,提高了并发性能。它还引入了树结构来优化链表,提高了查找性能。其在设计上更适合高度并发的情况,而且不再受限于分段锁的开销和复杂性。

13. ConcurrentHashMap在JDK1.8中,为什么要使用内置锁synchronized来代替重入锁ReentrantLock?🔒

🔑🔑🔑

  1. 性能改进: synchronized 在 JDK 1.8 中得到了很大的性能改进。JVM 在某些情况下可以自动地优化 synchronized 块,使其比之前更加高效。这种性能改进降低了使用 synchronized 的代价,并且在许多情况下,性能已经接近于使用 ReentrantLock,甚至更好。

  2. 代码简化: 使用 synchronized 可以简化代码,因为不再需要显式地创建和管理 ReentrantLock 对象。这减少了代码的复杂性,使得代码更容易理解和维护。

  3. 可读性: 使用 synchronized 可以使代码更具可读性,因为它是一种更常见的同步机制。这使得其他开发人员更容易理解代码,降低了代码的维护成本。

  4. 减少竞争: JDK 1.8 的 ConcurrentHashMap 内部结构经过了优化,减少了不必要的锁竞争,使得 synchronized 更具吸引力。此外,ConcurrentHashMap 在 JDK 1.8 中引入了一些新的技术,如 CAS 操作,以降低锁竞争。

总之,虽然 JDK 1.8 中的 ConcurrentHashMap 不再使用 ReentrantLock,而是使用内置锁 synchronized,但这个改变是经过深思熟虑的,主要出于性能改进、代码简化、可读性和竞争减少等因素的考虑。这使得 ConcurrentHashMap 在 JDK 1.8 中更具吸引力,能够更好地处理高度并发的情况。

14.什么是负载因子,为什么是0.75🔒

🔑🔑🔑

负载因子(Load Factor)是与哈希表(Hash Table)相关的一个概念,它用来衡量哈希表的空间利用率。负载因子是一个小数,通常表示为 λ(lambda),HashMap 扩容的计算公式是:

initialCapacity * loadFactor = HashMap 扩容,

 其中,initialCapacity 是初始容量,默认值为 16(懒加载机制,只有当第一次 put 的时候才创建),loadFactor 是负载因子,默认值为 0.75。也就是说当 16 * 0.75 = 12 时,HashMap 就会开始扩容。

负载因子的值在 0 到 1 之间,它反映了哈希表的填充程度。较小的负载因子表示哈希表较空,而较大的负载因子表示哈希表较满。通常,负载因子的选择会影响哈希表的性能和空间占用。

为什么负载因子通常选择为 0.75? 选择负载因子为 0.75 是一个经验性的决策,它在很多情况下被认为是一个良好的折衷值,具有以下优点:

  1. 空间效率与性能的平衡: 较低的负载因子会导致哈希表的空间占用更小,但可能需要更频繁的扩容操作,这可能会增加性能开销。较高的负载因子会提高空间利用率,减少扩容操作的频率,从而提高性能。

  2. 避免哈希冲突的频繁发生: 较高的负载因子可以减少哈希冲突的频率。如果负载因子太低,哈希表容量过大,元素分布稀疏,容易导致不必要的内存浪费和哈希冲突的不可预测性。

  3. 均衡的性能: 0.75 的负载因子通常在保持空间效率的同时,提供了良好的性能。这个值在实际应用中已被证明是一个合理的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1048707.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百数服务商——顶杰云,为建筑行业数字化转型“添砖加瓦”

杭州顶杰科技有限公司: 顶杰科技是国内一家行业领先的软件与信息技术服务提供商同时也是百数在建筑业的垂直领域服务商,通过低代码、云计算、AI大数据、IOT、区块链等先进的产品和技术,致力服务于政府、运营商、央国企、制造、金融、教育、能…

AI项目十:Swin Transformer目标检测环境搭建

若该文为原创文章,转载请注明原文出处。 Swin Transformer是做什么的这里不做介绍,主要是记录下学习的全过程,Swin Transformer在搭建和训练的过程中,折腾了很久,主要是在折腾环境。 一、AutoDL租用实例 个人没有GP…

多线程的学习中篇下

volatile 关键字 volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性” 示例代码: 运行结果: 当输入1(1是非O)的时候,但是t1这个线程并沿有结束循环, 同时可以看到,t2这个线程已经执行完了,而t1线程还在继续循环. 这个情况,就叫做内存可见性问题 ~~ 这…

初识Java 11-2 函数式编程

目录 高阶函数 闭包 函数组合 柯里化和部分求值 本笔记参考自: 《On Java 中文版》 高阶函数 ||| 高阶函数的定义:一个能接受函数作为参数或能把函数当返回值的函数。 把函数当返回值的情况: import java.util.function.Function;inter…

26551-2011 畜牧机械 粗饲料切碎机

声明 本文是学习GB-T 26551-2011 畜牧机械 粗饲料切碎机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了粗饲料切碎机的产品型号、技术要求、试验方法、检验规则、标志、包装、运输与贮存。 本标准适用于加工农作物秸秆等粗饲料…

Python接口自动化之unittest单元测试

以下主要介绍unittest特性、运行流程及实际案例。 一、单元测试三连问 1、什么是单元测试? 按照阶段来分,一般就是单元测试,集成测试,系统测试,验收测试。单元测试是对单个模块、单个类或者单个函数进行测试。 将…

迅为龙芯2K1000开发板通过汇编控制GPIO

上一小节,我们使用了 C 语言控制了 gpio,这一小节我们来看一下如何使用汇编来控制 gpio 呢?有的 同学可能会有疑问了,既然我们可以使用 C 语言来控制 gpio,为什么我们还要使用更底层的汇编语言呢, 如果我…

【Read View】Read View如何在MVCC里面工作、事务的隔离级别如何实现等重点知识汇总

目录 Read View 在 MVCC 里如何工作的 隔离级别如何实现的呢 Read View 在 MVCC 里如何工作的 ReadView用于管理事务之间的数据一致性,特别是在并发访问数据库时。 那 Read View 到底是个什么东西? Read View 有四个重要的字段: m_ids &…

Python函数:chr()和ord()

两个函数是基于Unicode编码表进行进行字符与字码之间的转换。 chr()函数是通过字码转换成字符: 如图,坐标(1,4e10)丑 使用chr需要线将坐标相加得到:4e11 chr默认传入10进制的字码. 如图是各进制的字码。 也可以传入其他进制,不过需要在前面传入的参数最前…

canvas+javascript实现点击更改图片中指定部位的颜色(将近似的颜色值变更为自定义的颜色)

原图&#xff1a; 完整代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" co…

前端架构师之02_ES6_高级

1 类和继承 1.1 class类 JavaScript 语言中&#xff0c;生成实例对象的传统方法是通过构造函数。 // ES5 创建对象 // 创建一个类&#xff0c;用户名 密码 function User(name,pass){// 添加属性this.name name;this.pass pass; } // 用 原型 添加方法 User.prototype.sho…

跨境电商如何利用海外代理IP,提高竞争力?

随着经济全球化的深度发展&#xff0c;跨境电商已经成为外贸发展的主要赛道&#xff0c;跨境电商王者般的新业态&#xff0c;近年来&#xff0c;我国跨境电商发展得到政府的大力扶持。而代理IP也逐渐成为助力跨境业务的强大工具之一。可以为我们跨境电商种出现的如地域限制、安…

中秋国庆,双节同乐

中秋国庆双节将至&#xff0c;送上最诚挚的祝福&#xff1a; 愿你的中秋月圆&#xff0c;家庭团圆更圆满&#xff0c;幸福围绕&#xff0c;笑声不断。 愿国庆日祖国繁荣昌盛&#xff0c;人民幸福安康&#xff0c;和谐美好永恒。 让我们共同庆祝这个特殊的时刻&#xff0c;祝福您…

MDETR:端到端多模态理解的调制检测

代码&#xff1a;https://github.com/ashkamath/mdetr 摘要 多模态推理系统依靠预训练的目标检测器从图像中提取感兴趣的区域。然而&#xff0c;这个关键模块通常用作黑匣子&#xff0c;独立于下游任务进行训练&#xff0c;并使用固定的对象和属性词汇表。这使得此类系统难以捕…

【数据仓库设计基础(三)】数据集市

文章目录 一. 数据集市的概念二. 数据集市与数据仓库的区别三. 数据集市设计 一. 数据集市的概念 数据集市是数据仓库的一种简单形式&#xff0c;通常由组织内的业务部门自己建立和控制。一个数据集市面向单一主题域&#xff0c;如销售、财务、市场等。 数据集市的数据源可以是…

Lucene-MergePolicy详解

简介 该文章基于业务需求背景&#xff0c;因场景需求进行参数调优&#xff0c;下文会尽可能针对段合并策略&#xff08;SegmentMergePolicy&#xff09;的全参数进行说明。 主要介绍TieredMergePolicy&#xff0c;它是Lucene4以后的默认段的合并策略&#xff0c;之前采用的合并…

网络时钟程序Net_Clock

网络时钟程序Net_Clock 小程序的源代码,着重编程思想,巧妙的构思架构... 主要功能:简单的Socket网络套接从时间服务器获取时间,timeSetEvent精度定时器,颜色方案,自绘标题栏,自绘菜单...等等 VS2015平台的精度时钟程序,北京,美东,美中三个时区日期时间显示,多种颜色方案显示…

深入理解常见应用级算法思想

1 概论 1.1 概念 1.1.1 数据结构 1&#xff09;概述 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效率。 2&#xff09;划分 从关注的维度…

洗文案神器,不同洗文案神器怎么选择

在数字时代&#xff0c;文字的重要性变得愈发突出&#xff0c;不论是在商业领域的广告宣传、自媒体的文章创作&#xff0c;还是学术界的研究论文&#xff0c;文字都是传递信息和思想的关键媒介。然而&#xff0c;创作高质量的文案并非易事&#xff0c;这就是为什么越来越多的人…

FFmpeg安装教程

一、下载安装 打开官方网站&#xff0c;ffmpeg管网地址&#xff0c;点击左侧download或页面中的绿色Download按钮。 选择要下载的平台&#xff0c;windows版本选择红框第一个&#xff0c;第二个是github平台 点击左侧release builds&#xff0c;选择要下载的软件版本&#x…