JAVA集合面试总结

目录

一、JAVA集合概述

1. 主要子接口及实现类

二、Collection子接口--List

1. ArrayList类

2. LinkedList类

3. ArrayList 类和 LinkedList 类的区别

三、Collection子接口--Set

1. HashSet类

2. TreeSet类

 3. HashSet类和TreeSet类的区别

四、Map集合

1. HashMap

1. HashMap的底层原理

 2. HashMap的默认长度是多少,为什么是这么多?

3. 为什么用红黑树,不是B/B+树,不是二叉树?

4. 为什么先用链表,再用红黑树,而不是直接用树?

5. HashMap线程不安全的表现

6. 如何保证HashMap的线程安全?

7. 为什么选择6和8作为链表化和树化的阈值

8. 为什么不直接用hash码作为数组table的下标?

9. 什么样类型的数据适合做hashmap的key?

10. 为什么HashMap不用LinkedList,而选用数组?

11. hashmap中get元素的过程

12. hashmap中put元素的过程

13. 还有哪些hash算法

14. Hashmap中红黑树按照什么排序

2. HashSet

1. HashSet和HashMap的区别

2. HashSet如何判断检查重复

3. HashTable

1. HashTable和HashMap的区别

2. 为什么Hashtable不允许key和value为null呢?为什么这么设计呢

4. ConcurrentHashMap

1. jdk 1.7 的ConcurrentHashMap

2. jdk1.8 的ConcurrentHashMap

3. ConcurrentHashMap和HashTable的区别?

5. Collections 工具类

排序操作:

查找替换操作:


一、JAVA集合概述

为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。


1. 主要子接口及实现类

对于Collection 接口,下面又有三个主要的子接口:ListSet 和 Queue

List:是最常用的接口。是有序集合,允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置,用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,与数组类似。

Set:存储的元素是无序的,不能包含重复的元素。

Queue:Queue 是 Java 提供的队列实现,有点类似于 List。 按特定的排队规则来确定先后顺序。

Map:使用键值对(key-value)存储,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值。

对于 Set、List、Queue 和 Map 这 4 种集合,Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。


二、Collection子接口--List

List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。

List 实现了 Collection 接口,它主要有两个常用的实现类:ArrayList 类和 LinkedList 类。


1. ArrayList类

底层数据结构:数组

ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。

ArrayList<String> list = new ArrayList<>();
list.add(); // 添加元素到末尾
list.addAll(); // 添加另一集合中的所有元素到末尾
list.get(); // 访问随机元素,根据索引
list.set(0,"修改元素"); // 第一个参数为需要修改元素的索引,第二个参数为修改的值
list.remove(0); // 参数为元素的索引
list.size(); // 元素数量
list.isEmpty(); // 判断是否为空

list.forEach(l->{
    System.out.println("forEach---"+l);
});   // foreach(Lambda 表达式)


2. LinkedList类

底层数据结构:双向链表

LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。

addFirst(E e) :在此列表的开始处插入指定的元素。
addLast(E e) :将指定的元素列表的结束。
getFirst() :返回此列表中的第一个元素。
getLast() :返回此列表中的最后一个元素。
removeFirst() :移除并返回此列表中的最后一个元素。
removeLast() :移除并返回此列表中的最后一个元素。

3. ArrayList 类和 LinkedList 类的区别

  • 底层数据结构:ArrayList内部是数组结构实现,LinkedList是双向链表结构,对每一个元素都有指向前后元素的指针。
  • 插入和删除是否受元素位置的影响:ArrayList插入和删除元素的时间复杂度受元素位置的影响。因为在指定位置插入数据和删除需要对数组复制和重排序。LinkedList采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响。
  • 数据读取效率:ArrayList查找元素效率高。LinkedList顺序读取效率比较高,随机读取元素效率比较低。
  • 内存空间占用:ArrayList 的空间浪费主要体现在在 List 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

另外,不要下意识地认为 LinkedList 作为链表就最适合元素增删的场景。

LinkedList 仅仅在头尾插入或者删除元素的时候时间复杂度近似 O(1),其他情况增删元素的时间复杂度都是 O(n) 。


三、Collection子接口--Set

Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。

Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类


1. HashSet类

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。

底层结构:Hashset底层使用的是HashMap哈希表结构储存,所有放入HashSet中的集合元素实际上由HashMap的key来保存,而对应的value则存储了一个PRESENT,它是一个静态的Object对象。

HashSet 具有以下特点:

  • 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。

  • HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。

  • 集合元素值可以是 null。

如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。

也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。


2. TreeSet类

TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。

TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。

底层:TreeMap树结构存储


 3. HashSet类和TreeSet类的区别

相同点:单例集合,数据不可重复

不同点1:底层使用的储存数据结构不同:

Hashset底层使用的是HashMap哈希表结构储存

Treeset底层用的是TreeMap树结构储存。

不同点2:储存的数据保存唯一方式不同:

Hashset是通过复写hashCode()方法和equals()方法来保证的。

Treeset是通过Compareable接口的compareto方法来保证的。

不同点3:hashset无序 Treeset有序


四、Map集合

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。

Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。

Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。

Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。


1. HashMap

1. HashMap的底层原理

JDK 1.7 中HashMap的底层数据结构是数组+链表,使用Entry类存储Key和Value;

JDK 1.8中HashMap的底层数据结构是数组+链表/红黑树,使用Node类存储Key和Value。

这里的Entry和Node并没有什么不同

因为HashMap本身所有的位置都为null,所以在插入元素的时候即put操作时,会根据key的hash()方法得到一个数字,用这个数字去计算出一个index值,也就是这个元素将要插入的位置,找到这个位置的链表,会和链表中的key去使用equals进行比较,如果相等则直接更新value值,不相等就把新的k-v值插入链表中去。

数组的长度是有限的,在有限的数组上使用哈希,那么哈希冲突不可避免,很可能两个元素计算得出的index是相同的,所以链表的引入:拉链法,就是为了解决哈希冲突。把哈希值相同的元素放在同一条链表上。

当然,如果哈希冲突严重,链表会越来越长,因为链表不支持索引,所以找元素需要遍历一遍链表,效率低下。所以JDK 1.8引入了红黑树,当链表长度大于8,同时数组的长度大于64,会转换为红黑树

如果数组长度小于64,那优先选择对数组扩容resize,而不是转换链表。

如果当前存入的数据数量大于Capacity*LoadFactor(0.75) 的时候,就会进行数组扩容resize.

扩容resize分为两步:

  1. 扩容:创建一个新的Entry/Node空数组,长度是原数组的2倍

  2. ReHash:遍历原Entry/Node数组,把所有的Entry/Node节点重新Hash到新数组

ReHash的原因是数组长度改变后,hash的规则也会改变
index计算公式是:index = HashCode(key) & (Length-1)

1.7是头插法,多线程环境下容易发生循环链表。JDK 1.8改成了尾插法,扩容时会保持链表原本的顺序。


 2. HashMap的默认长度是多少,为什么是这么多?

默认数组长度是16,其实只要是2的次幂都行,至于为啥是16呢,我觉得应该是个经验值问题,Java作者是觉得16这个长度最为常用。  

为什么是2的次幂呢?

常用的Hash函数是这样的:index= HashCode(key)%Length,但是因为位运算的效率比较高嘛,所以HashMap 就相应的改成了这样:index= HashCode(key)&(Length-1) 那么为了保证根据上述公式计算出来的index值是分布均匀的,我们就必须保证Length是2的次幂。

进一步解释:

2的次幂,也就是2的n次方,它的二进制表示就是1后面跟着n个0,那么2的n次方-1的二进制表示就是n个1。而对于&操作来说,任何数与1做&操作的结果都是这个数本身。也就是说,index的结果等同于HashCode(key)后n位的值,只要HashCode本身是分布均匀的,那么我们这个Hash算法的结果就是均匀的。


3. 为什么用红黑树,不是B/B+树,不是二叉树?

  • 红黑树是二叉查找树的一种,它的时间复杂度O(logn),好于列表的O(n)

  • 如果用B/B+树的话,在数据量不是很多的情况下,数据都会“挤在”一个结点里面,这个时候遍历效率就退化成了链表。

  • 二叉树在数据有序的情况下,会变成一条线性结构,使得查找效率变慢,而红黑树可以通过左旋、右旋、变色等操作保持平衡。能解决二叉树的缺陷。


4. 为什么先用链表,再用红黑树,而不是直接用树?

主要是对时间和空间的折中考虑,在hash冲突比较小的时候,即当元素小于8个,此时做查询操作,链表结构已经能保证查询性能。如果引入树反而效率会降低。

当元素大于8个的时候,此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。 因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。


5. HashMap线程不安全的表现

关于JDK 1.7中HashMap的线程不安全,上面已经说过了,就是会出现环形链表。虽然JDK 1.8采用尾插法避免了循环链表的问题,但是它仍然是线程不安全的

  • JDK 1.7:由于采用头插法改变了链表上元素的的顺序,并发环境下扩容可能导致「循环链表」的问题

  • JDK 1.8:由于put操作并没有上锁,并发环境下可能发生某个线程插入的「数据被覆盖」的问题

假设线程1和线程2同时进行put操作,恰好这两条不同的数据的hash值是一样的,并且该位置数据为null,这样,线程1和线程2都会进入这段代码进行插入元素。假设线程1进入后还没有开始进行元素插入,就被挂起,而线程2正常执行,并且正常插入数据,随后线程1得到CPU调度进行元素插入,这样,线程2插入的数据就被覆盖了。

非常像数据库事务并发执行时丢失修改的情况。


6. 如何保证HashMap的线程安全?

一般有三种方式来代替原生的线程不安全HashMap

  • 使用 java.util.Collections类的synchronizedMap方法包装一下HashMap,得到线程安全的HashMap,其原理就是对所有的修改操作都加上 synchronized。方法如下:public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)

  • 使用线程安全的HashTable类代替,该类在对数据操作的时候都会上锁,也就是加上synchronized。

  • 使用线程安全的ConcurrentHashMap类代替,该类在JDK 1.7和JDK 1.8的底层原理有所不同,JDK 1.7采用数组+链表存储数据,使用分段锁Segment保证线程安全;JDK 1.8采用数组+链表/红黑树存储数据,使用CAS + synchronized 保证线程安全。

不过前两者的线程并发度并不高,容易发生大规模阻塞,所以一般使用的都是ConcurrentHashMap,他的性能和效率明显高于前两者。


7. 为什么选择6和8作为链表化和树化的阈值

  • 首先是泊松分布概率选择了6,8是效率最好的取值;

  • 有个差值7可以防止链表和树的频繁切换。


8. 为什么不直接用hash码作为数组table的下标?

  • 哈希码一般是int型,范围是 -(2^31) ~ (2^31-1),容易出现哈希码和数组大小范围不匹配的情况,即计算出来的哈希码可能不在数组大小范围内,从而导致无法匹配存储位置。

  • 常见解决办法就是hash值与数组长度取模。(与数组长度减一做位与运算)


9. 什么样类型的数据适合做hashmap的key?

像Integer这种,内部属性value被final修饰,保证了Hash值的不可更改性,有效的减少了hash冲突。


10. 为什么HashMap不用LinkedList,而选用数组?

因为用数组效率最高。

在HashMap中,定位桶的位置是利用元素的key的哈希值对数组长度取模得到。此时,我们已得到桶的位置。显然数组的查找效率比LinkedList大。

那ArrayList,底层也是数组,查找也快啊,为啥不用ArrayList? 因为采用基本数组结构,扩容机制可以自己定义,HashMap中数组扩容刚好是2的次幕,在做取模运算的时候效率高。 而ArrayList的扩容机制是1.5倍扩容。


11. hashmap中get元素的过程

对key的hashCode0做hash运算,计算index;

如果在bucket里的第一个节点里直接命中,则直接返回;

如果有冲突,则通过key.equals(k)去查找对应的Entry;

若为树,则在树中通过key.equals(k)查找,O(logn);

若为链表,则在链表中通过key.equals(k)查找,O(n)。


12. hashmap中put元素的过程

对key的hashCodeo做hash运算,计算index;

如果没碰撞直接放到bucket里;

如果碰撞了,以链表的形式存在buckets后;

如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树(JDK1.8中的改动);

如果节点已经存在就替换old value(保证key的唯一性)

如果bucket满了(超过load factor*current capacity),就要resize.


13. 还有哪些hash算法

Hash算法是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。

比较出名的有MurmurHash、MD4、MD5等等。


14. Hashmap中红黑树按照什么排序

  • 如果key实现了compareable接口,那可以利用compare排序;

  • 如果没有实现接口,按照String排序,通过比较类名;

  • 如果类名字一样就按照hashcode排序。


2. HashSet

1. HashSet和HashMap的区别

HashSet的底层就是基于HashMap实现的。大部分方法都是通过调用HashMap的方法来实现。

HashMapHashSet
实现Map接口实现Set接口
存储键值对存储元素对象
调用put方法新增元素调用add方法新增元素
使用key计算hashcode使用成员对象来计算hashcode,对于两个对象来说 hashcode 可能相同,所以用 equals() 方法用来判断对象的相等性 

2. HashSet如何判断检查重复

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。

但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。

hashcode()equals()的相关规定:

  • 如果两个对象相等,则hashcode一定也是相同的;
  • 两个对象相等,对两个equals()方法返回true;
  • 两个对象有相同的hashcode值,它们也不一定是相等的。

综上,如果一个类的equals()方法被覆盖过,则hashCode()方法也必须被覆盖。
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),即使通过equals()判断为相同的两个对象,在加入HashSet时,也不会被HashSet认为是重复对象。

JDK8中,HashSet 的 add() 方法只是简单的调用了 HashMap 的 put() 方法,并且判断了一下返回值以确保是否有重复元素。直接看一下HashSet中的源码

// Returns: true if this set did not already contain the specified element
// 返回值:当set中没有包含add的元素时返回真
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
}

 实际上无论HashSet 中是否已经存在了某元素,HashSet 都会直接插入,只是会在 add() 方法的返回值处告诉我们插入前是否存在相同元素。


3. HashTable

1. HashTable和HashMap的区别

  • 线程安全:HashMap是非线程安全的,HashTable是线程安全的,因为Hashtable非常粗暴的给每个方法都加上了悲观锁synchronized。
  • 效率:因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它。
  • 对 Null key 和 Null value 的支持:Hashtable是不允许 key 或 value 是 null 的,HashMap 的key 和 value 都可以为 null,但是null作为键只能有一个。
  • 底层数据结构:JDK1.8以后的HashMap解决哈希冲突,会有扩容转红黑树的机制,但是hashtable没有这样的机制。
  • 初始容量大小和每次扩充容量大小的不同:1)不设定容量初始值,Hashtable默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。2)设定容量初始值后,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小。

原理解释:

HashMap的key和value都可以为null

1)HashMap相比Hashtable做了一个特殊的处理,如果我们put进来的key是null,HashMap在计算这个key的hash值时,会直接返回0;
也就是说HashMap中key为null的键值对的hash为0。因此一个HashMap对象中只会存储一个key为null的键值对,因为它们的hash值都相同。

2)如果我们put进来的value是null,由于HashMap的put方法不会对value是否为null进行校验,因此一个HashMap对象可以存储多个value为null的键值对。


2. 为什么Hashtable不允许key和value为null呢?为什么这么设计呢

Hashtable和ConcurrentHashMap都不允许,是出于对并发安全性的考虑。

当你通过get(key)获取到对应的value时,如果返回的结果是null 时,你无法判断这个key是否真的存在。为此,我们需要调用containsKey方法来判断这个key到底是value=null 还是它根本就不存在,如果containsKey 方法返回的结果是true,OK,那我们就可以调用 map.get(key)获取value.

这个过程对于单线程的HashMap没有任何问题。

!!但是,由于Hashtable和ConcurrentHashMap是支持多线程的容器。

我们假设此时某个线程A调用了map.get(key)方法,它返回为value=null的真实情况就是因为这个key不存在。当然,线程A还是会按部就班的继续用map.containsKey(key),我们期望的结果是返回false。但是,在线程A调用map.get(key)方法之后,map.containsKey方法之前,另一个线程B执行了map.put(key,null)的操作。那么线程A调用的map.containsKey方法返回的就是true了。这就与我们的假设的真实情况不符合了。

所以,出于并发安全性的考虑,Hashtable和ConcurrentHashMap不允许key和value为null。


4. ConcurrentHashMap

在并发使用到HashMap的时候,往往不建议直接用HashMap,因为HashMap在并发写数据的时候容易因为rehash的过程产生环形链表的情况。所以在并发使用Map结构时,一般建议使用ConcurrentHashMap。

1. jdk 1.7 的ConcurrentHashMap

在JDK1.7中ConcurrentHashMap采用了Segment+HashEntry+Reentrantlock的方式实现。

Segment(分段锁):ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承ReentrantLock)

内部结构:ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。如下图是ConcurrentHashMap的内部结构图

从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作。第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部。  


2. jdk1.8 的ConcurrentHashMap

JDK1.8中ConcurrentHashMap参考了JDK8 HashMap的实现,采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作。并发控制使用synchronized和CAS来操作。整个看起来就像是优化过且线程安全的HashMap,

JDK1.8的Node节点中value和next都用volatile修饰,保证并发的可见性。 可以理解为,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。


3. ConcurrentHashMap和HashTable的区别?

ConcurrentHashMap 和 HashTable 的区别主要体现在实现线程安全的方式上不同。

  • 底层数据结构:JDK1.7 的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构是数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
  • 实现线程安全的方式:
  •  ① 在 JDK1.7 的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 到了 JDK1.8 的时候已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。整个看起来就像是优化过且线程安全的 HashMap
  • ② HashTable(同一把锁) :使用 synchronized  来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

5. Collections 工具类

Collections 工具类常用方法:

  1. 排序
  2. 查找,替换操作

排序操作:

void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面

查找替换操作:

int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target)
boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值