【Java学习笔记】#01集合

目录

一.List,Set,Map的区别

1. **List**:

2. **Set**:

3. **Map**:

总结:

二.List , Set , Map 在 Java 中分别由哪些对应的实现类?底层的数据结构?

1. **List**:

2. **Set**:

3. **Map**:

三.有哪些集合是线程不安全的?怎么解决呢

1. **ArrayList**:

2. **HashSet**:

3. **HashMap**:

解决方法:

四.HashMap 查询,删除的时间复杂度

1. **没有哈希冲突的情况**:

2. **转链表的情况**:

3. **链表转红黑树的情况**:

五.HashMap 的底层实现

**JDK 1.8之前**:

**JDK 1.8之后**:

六.HashMap 的⻓度为什么是 2 的幂次⽅

1. **提高运算效率**:

2. **优化哈希冲突**:

3. **性能优势**:

七.HashSet、LinkedHashSet和TreeSet的异同

1.**HashSet**:

-底层数据结构:

- 特点:

- 应用场景:

2.**LinkedHashSet**:

- 底层数据结构:

- 特点:

3.**TreeSet**:

- 底层数据结构:

- 特点:

- 应用场景:

八.HashMap 和 Hashtable 的区别? HashMap 和 HashSet 区别? HashMap 和 TreeMap 区别? ConcurrentHashMap 和 Hashtable 的区别?

**HashMap和Hashtable的区别**:

1. **底层数据结构**:

2. **线程安全**:

**HashMap和HashSet的区别**:

1. **底层数据结构**:

2. **元素特性**:

**HashMap和TreeMap的区别**:

1. **底层数据结构**:

2. **元素特性**:

**ConcurrentHashMap和Hashtable的区别**:

1. **底层数据结构**:

2. **线程安全实现方式**:

3. **性能比较**:

九.ConcurrentHashMap 线程安全的具体实现⽅式/底层具体实现

**JDK 1.7中的实现方式**:

**JDK 1.8中的实现方式**:


一.List,Set,Map的区别

1. **List**:

  •    - 元素有序:List中的元素按照插入顺序排列,可以根据索引访问元素。
  •    - 元素可以重复:List允许存储重复的元素,可以在列表中存在相同的值。
  •    - 存储的元素类型:List可以存储各种类型的元素对象。

2. **Set**:

  •    - 元素无序:Set中的元素没有固定的顺序,不能通过索引访问元素。
  •    - 元素不可重复:Set中的元素是唯一的,不能在Set中存储相同的值。即使尝试添加重复元       素,Set也不会存储它。
  •    - 存储的元素类型:Set也可以存储各种类型的元素对象。

3. **Map**:

  •    - 元素无序:Map中的键值对没有固定的顺序,无法通过索引访问元素。
  •    - 键不可重复:Map中的键是唯一的,不可重复的,每个键只能关联一个值。
  •    - 存储的元素类型:Map存储的是键值对,每个元素都包含一个键和一个与之相关联的值。       键和值可以是任何类型的对象。

总结:

  • - List是有序的可重复集合。
  • - Set是无序的不可重复集合。
  • - Map是键值对的映射表,键是不可重复的,值可以重复。

根据使用的需求,可以选择适合的数据结构。如果需要保持元素的顺序并允许重复,则使用List。如果需要唯一元素集合且不关心元素顺序,则使用Set。如果需要通过键来查找值的映射关系,则使用Map。

二.List , Set , Map 在 Java 中分别由哪些对应的实现类?底层的数据结构?

在Java中,List、Set和Map都是接口,它们有许多对应的实现类。以下是它们的常见实现类以及它们的底层数据结构:

1. **List**:

  •    - ArrayList:底层数据结构是数组,支持动态扩展和快速随机访问。
  •    - LinkedList:底层数据结构是双向链表,支持高效的插入和删除操作。
  •    - Vector:类似于ArrayList,但线程安全,性能较差,通常不推荐使用。

2. **Set**:

  •    - HashSet:底层数据结构是哈希表,不保证元素顺序,允许存储null值。
  •    - LinkedHashSet:底层数据结构是哈希表和双向链表的结合,保证元素按插入顺序排列。
  •    - TreeSet:底层数据结构是红黑树,保证元素有序(按照比较器或元素自然排序),不允         许存储null值。

3. **Map**:

  •    - HashMap:底层数据结构是哈希表,使用键值对存储元素,不保证键值对的顺序,允许键       和值都为null。
  •    - LinkedHashMap:底层数据结构是哈希表和双向链表的结合,保证按插入顺序迭代键值           对。
  •    - TreeMap:底层数据结构是红黑树,按键的排序顺序迭代键值对。

这些实现类都位于`java.util`包中,是Java集合框架的

三.有哪些集合是线程不安全的?怎么解决呢

在Java集合框架中,有一些集合类是线程不安全的,这意味着它们在多线程环境下使用时,可能会导致并发问题。以下是一些线程不安全的集合类:

1. **ArrayList**:

在多线程环境下,如果多个线程同时修改ArrayList,可能会导致数据不一致或抛出异常。

2. **HashSet**:

当多个线程同时修改HashSet时,可能导致HashSet的结构发生变化,导致无法预测的结果。

3. **HashMap**:

与HashSet类似,多个线程同时修改HashMap可能导致HashMap的结构发生变化,产生不确定的结果。

解决方法:

解决线程不安全问题的一种常见方法是使用线程安全的集合类,这些集合类在多线程环境下提供了同步机制,确保多个线程可以安全地访问和修改集合。在Java中,可以通过`Collections`类的静态方法来获取线程安全的集合,如下所示:

1. 使用`Collections.synchronizedList()`来获取线程安全的List:

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

2. 使用`Collections.synchronizedSet()`来获取线程安全的Set:

Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());

3. 使用`Collections.synchronizedMap()`来获取线程安全的Map:

Map<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());

以上方法会返回一个线程安全的集合,它在底层对所有的读写操作进行了同步处理,确保了在多线程环境下的安全访问。

另一种解决方法是使用Java集合框架中的并发集合类,这些集合类专门设计用于高并发环境,提供更好的性能和可伸缩性。例如,Java中提供了`ConcurrentHashMap`、`CopyOnWriteArrayList`和`ConcurrentSkipListSet`等线程安全的集合类,它们在高并发场景下表现较好。选择哪种解决方法取决于具体的使用场景和性能需求。

四.HashMap 查询,删除的时间复杂度

在HashMap中,查询和删除操作的时间复杂度取决于底层的数据结构和哈希冲突的情况。根据不同情况,可以分为以下几种:

1. **没有哈希冲突的情况**:

  •    - 时间复杂度:O(1)
  •    - 在理想情况下,如果没有哈希冲突,HashMap的查询和删除操作的时间复杂度是常数时间        复杂度O(1),即平均情况下,它们的操作是非常快速的。

2. **转链表的情况**:

  •    - 时间复杂度:O(n)
  •    - 当发生哈希冲突,即两个不同的键被映射到同一个桶(bucket)时,HashMap会在该桶上       使用链表来存储这些键值对。在这种情况下,查询和删除操作的时间复杂度会退化为               O(n),其中n是链表的长度。

3. **链表转红黑树的情况**:

  •    - 时间复杂度:O(log n)
  •    - 为了避免链表过长导致性能下降,当链表长度达到一定阈值时,HashMap会将链表转换为       红黑树(自平衡二叉搜索树)。在红黑树中,查询和删除操作的时间复杂度将变为O(log         n),其中n是红黑树的节点数。

综上所述,如果HashMap中没有哈希冲突,则查询和删除操作的时间复杂度是常数时间复杂度O(1)。但是,一旦发生哈希冲突,时间复杂度可能会退化到O(n)(链表情况)或O(log n)(红黑树情况)。因此,在设计HashMap时,尽量避免哈希冲突,或者选择适当的哈希函数和扩容策略,以确保在实际使用中获得较好的性能表现。

五.HashMap 的底层实现

在JDK 1.8之前,HashMap的底层实现主要由数组和链表组成,用于处理哈希冲突。JDK 1.8之后,为了优化处理长链表导致的性能问题,HashMap引入了红黑树的数据结构。

下面是HashMap在JDK 1.8之前和之后的底层实现简要说明:

**JDK 1.8之前**:

  • - 数组:HashMap内部使用一个数组来存储键值对(Entry)。
  • - 链表:当两个不同的键映射到同一个数组位置时,HashMap会将它们以链表的形式连接在一起。每个链表节点都包含一个键值对。
  • - 哈希冲突解决:通过哈希函数计算键的哈希码,根据哈希码找到对应的数组索引位置。如果多个键的哈希码相同,它们将以链表形式连接在同一个数组位置上。

**JDK 1.8之后**:

  • - 红黑树:在JDK 1.8中,当链表的长度达到一定阈值(默认为8)时,HashMap会将链表转换为红黑树,以提高查找、插入和删除操作的性能。红黑树是一种自平衡的二叉搜索树,它可以保持较好的性能,即使在较大的链表情况下也能保持O(log n)的时间复杂度。
  • - 优化:引入红黑树后,对于较长的链表,查询和删除操作的时间复杂度将从O(n)降低到O(log n),从而提高HashMap在哈希冲突严重时的性能。

JDK 1.8之后的HashMap在处理哈希冲突时,优先使用红黑树而不是链表,从而避免了链表过长带来的性能问题。这使得HashMap在更广泛的场景下都能保持较好的性能表现。

六.HashMap 的⻓度为什么是 2 的幂次⽅

HashMap的长度选择为2的幂次方,是为了提高运算效率和优化HashMap在哈希冲突时的表现。以下是具体的解释:

1. **提高运算效率**:

  •    - 当HashMap的长度为2的幂次方时,计算键的哈希码与数组长度进行与运算时,可以用位操作代替较慢的取余运算。因为数组的长度总是2的幂次方,所以`length - 1`的二进制表示形式全部是1,如:`1111`、`111111`等。这样的话,`hash & (length - 1)`就相当于对`hash`取模(`hash % length`),但是位操作效率更高,提高了运算速度。

2. **优化哈希冲突**:

  •    - 当数组长度为2的幂次方时,`length - 1`的二进制表示形式中每一位都是1,这样在进行哈希运算时,可以保证哈希码的每一位都会对应到数组的某个位置上,尽可能地分散元素在数组中的分布,减少哈希冲突的概率。

3. **性能优势**:

  •    - 使用长度为2的幂次方的数组,可以更加高效地进行哈希计算和扩容操作,进一步提升HashMap的性能。

七.HashSet、LinkedHashSet和TreeSet的异同

HashSet、LinkedHashSet和TreeSet都是Java集合框架中Set接口的实现类,它们都保证了元素的唯一性,且都不是线程安全的。它们的主要区别在于底层数据结构不同,因此适用的应用场景也有所不同。

1.**HashSet**:

-底层数据结构:

哈希表(HashMap)


- 特点:

  •   - 无序集合,不保留插入顺序。
  •   - 使用哈希表进行存储,通过哈希码确定元素的位置,所以查找和插入的时间复杂度为O(1)。
  •   - 不允许存储重复元素。
  •   - 可以存储一个null元素。

- 应用场景:

  •   - 适用于需要快速查找和插入元素,且不关心元素的顺序的情况。

2.**LinkedHashSet**:


- 底层数据结构:

  • 哈希表和双向链表的结合(LinkedHashMap)

- 特点:

  •   - 按照元素插入的顺序维护元素的顺序,是有序集合。
  •   - 使用哈希表进行存储,通过哈希码确定元素的位置,双向链表维护插入顺序。
  •   - 不允许存储重复元素。
  •   - 可以存储一个null元素。
  • - 应用场景:
  •   - 在需要保持元素插入顺序的同时,保持高效的插入和查找操作的情况下使用。

3.**TreeSet**:


- 底层数据结构:

  • 红黑树(Red-Black Tree)

- 特点:

  •   - 有序集合,元素根据自然排序或者指定的比较器排序。
  •   - 使用红黑树进行存储,插入、删除和查找操作的时间复杂度为O(log n)。
  •   - 不允许存储重复元素。
  •   - 不允许存储null元素。

- 应用场景:

  •   - 在需要有序集合并且需要快速插入、删除和查找操作的情况下使用。

综上所述,HashSet适用于无序、快速查找和插入元素的场景;LinkedHashSet适用于有序、保持插入顺序的场景;TreeSet适用于有序、快速插入、删除和查找元素的场景。选择合适的集合类型取决于具体的需求和使用场景。

八.HashMap 和 Hashtable 的区别? HashMap 和 HashSet 区别? HashMap 和 TreeMap 区别? ConcurrentHashMap 和 Hashtable 的区别?

**HashMap和Hashtable的区别**:

1. **底层数据结构**:

  •    - HashMap使用数组和链表(或红黑树)实现,支持null键和null值。JDK 1.8之后,链表长度达到一定阈值时会转换为红黑树。
  •    - Hashtable使用数组和链表实现,不支持null键和null值,任何一个键或值为null都会抛出NullPointerException。

2. **线程安全**:

  •    - HashMap是非线程安全的,在多线程环境下需要额外的同步措施来保证线程安全。
  •    - Hashtable是线程安全的,所有操作都是同步的,适用于多线程环境。但是,由于全表锁定,效率较低,不推荐在高并发情况下使用。

**HashMap和HashSet的区别**:

1. **底层数据结构**:

  •    - HashMap底层使用数组和链表(或红黑树)实现,是键值对的映射表。
  •    - HashSet底层使用HashMap实现,只是将元素都放在了HashMap的键上。

2. **元素特性**:

  •    - HashMap存储键值对,需要同时提供键和值。
  •    - HashSet只存储元素,不需要键值对,元素在HashSet中是唯一的,不允许重复。

**HashMap和TreeMap的区别**:

1. **底层数据结构**:

  •    - HashMap底层使用数组和链表(或红黑树)实现,是键值对的映射表。
  •    - TreeMap底层使用红黑树实现,是有序的键值对映射表,根据键的自然排序或者指定的比较器排序。

2. **元素特性**:

  •    - HashMap元素无序,根据键的哈希码确定存储位置。
  •    - TreeMap元素有序,根据键的自然排序或者比较器排序。

**ConcurrentHashMap和Hashtable的区别**:

1. **底层数据结构**:

  •    - ConcurrentHashMap在JDK 1.8之前使用分段锁(Segment),JDK 1.8之后使用数组和链表(或红黑树)实现。
  •    - Hashtable底层使用数组和链表实现,所有操作都是同步的,适用于多线程环境。

2. **线程安全实现方式**:

  •    - ConcurrentHashMap在JDK 1.8之前使用分段锁来实现高效的并发操作,将数据分成一段一段,每一段都对应一把锁,减少锁竞争。
  •    - JDK 1.8之后,ConcurrentHashMap使用了CAS和synchronized来实现并发操作,底层结构也发生了改变,利用红黑树和链表来替代分段锁。

3. **性能比较**:

  •    - ConcurrentHashMap在高并发情况下性能优于Hashtable,因为ConcurrentHashMap采用分段锁或CAS来减少锁竞争,而Hashtable是全表锁定的方式。

总结:HashMap、HashSet、TreeMap等都是不同的Java集合类,每个类有其特定的应用场景和性能特点,根据具体需求选择合适的集合类是很重要的。同时,对于多线程环境下的使用,需要注意选择合适的线程安全集合类,如ConcurrentHashMap。

九.ConcurrentHashMap 线程安全的具体实现⽅式/底层具体实现

在JDK 1.7和JDK 1.8中,ConcurrentHashMap的线程安全实现方式有所不同:

**JDK 1.7中的实现方式**:


在JDK 1.7中,ConcurrentHashMap使用了Segment分段锁的机制来保证线程安全。Segment是一种类似于HashMap的结构,内部维护一个数组,每个数组元素是一个HashEntry链表的头节点。每个Segment都拥有一把锁,不同的Segment之间可以并发执行,实现了对不同数据段的并行操作。

具体的实现流程如下:
1. ConcurrentHashMap在初始化时创建一组Segment,每个Segment维护一个数据段。
2. 根据键的哈希值确定应该放在哪个Segment中。
3. 在对数据进行插入、更新和删除操作时,只需要锁住对应的Segment,而不会影响到其他Segment的操作。

这种分段锁的机制使得ConcurrentHashMap在高并发情况下表现较好,因为不同线程可以同时操作不同的Segment,减少了锁的竞争。

**JDK 1.8中的实现方式**:


在JDK 1.8中,ConcurrentHashMap使用了更加先进的技术,主要依赖CAS(Compare and Swap)和synchronized来保证线程安全。

具体的实现流程如下:
1. ConcurrentHashMap使用数组和链表(或红黑树)实现桶(bucket)。
2. 桶中的每个节点是一个键值对(Node),每个Node使用CAS操作来进行插入和更新操作,从而避免了全局锁的使用。
3. 当链表长度达到一定阈值时,会将链表转换为红黑树,以提高查找、插入和删除操作的性能。
4. 在进行扩容和重新哈希操作时,使用synchronized来确保线程安全。

这种CAS和synchronized结合的实现方式使得ConcurrentHashMap在高并发情况下仍然能够保证线程安全,并且性能表现也较为优秀。

总结:
ConcurrentHashMap是Java集合框架中线程安全的哈希表实现。在JDK 1.7中使用Segment分段锁实现,并在JDK 1.8中引入了更加高效的CAS和synchronized结合的方式来保证线程安全。这使得ConcurrentHashMap成为在多线程环境下高效的数据结构。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值