41. ArrayList 和 LinkedList 的区别是什么?
ArrayList 和 LinkedList 都是 Java 中常用的 List 接口的实现,用于存储一系列动态的元素集合。它们之间的主要区别在于数据结构、性能特性、和适用场景。
- 数据结构:
- ArrayList 使用动态数组来实现 List 接口。这意味着所有元素都被连续存储在内存中,允许通过索引快速访问任何位置的元素。
- LinkedList 使用双向链表实现 List 接口。每个元素(节点)都包含数据以及指向前一个和后一个节点的引用,这样可以在不连续的内存位置中存储元素。
- 性能特性:
- 访问元素:ArrayList 在常量时间 O(1) 内提供对元素的随机访问,因为它是基于索引的。而 LinkedList 需要线性时间 O(n),因为它必须从头节点或尾节点遍历链表。
- 插入和删除:对于 ArrayList,插入和删除元素可能需要线性时间 O(n),因为它可能涉及到移动大量元素以保持数组的连续性。而 LinkedList 在这种情况下通常有更好的性能,为 O(1),因为只需要改变节点的指针。
- 内存占用:由于链表结构,LinkedList 在每个元素之间存储额外的引用,因此相比于 ArrayList,它在内存使用上稍微要高一些。
- 适用场景:
- ArrayList 更适用于需要频繁随机访问元素的场景,如数组遍历、基于索引的查找等。
- LinkedList 更适合在频繁插入和删除操作的场景中,如实现栈、队列等数据结构,或者当列表很大且操作频繁时,插入和删除的常数时间操作使它成为更合适的选择。
以下是一个简单的代码示例,展示两者的部分操作性能差异:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class ListPerformance {public static void main(String[] args) {List<Integer> arrayList = new ArrayList<>();List<Integer> linkedList = new LinkedList<>();// 添加元素for (int i = 0; i < 100000; i++) {arrayList.add(i);linkedList.add(i);}// ArrayList 访问元素快long startTime = System.currentTimeMillis();arrayList.get(50000); // 假设我们获取中间的元素long endTime = System.currentTimeMillis();System.out.println("ArrayList get operation time: " + (endTime - startTime) + "ms");// LinkedList 访问元素慢startTime = System.currentTimeMillis();linkedList.get(50000); // 假设我们获取中间的元素endTime = System.currentTimeMillis();System.out.println("LinkedList get operation time: " + (endTime - startTime) + "ms");// LinkedList 插入和删除快startTime = System.currentTimeMillis();linkedList.add(50000, 123456); // 在中间插入元素endTime = System.currentTimeMillis();System.out.println("LinkedList add operation time: " + (endTime - startTime) + "ms");startTime = System.currentTimeMillis();linkedList.remove(50000); // 删除中间的元素endTime = System.currentTimeMillis();System.out.println("LinkedList remove operation time: " + (endTime - startTime) + "ms");}
}
以上代码只是性能测试的一个简单示例,实际性能会根据具体操作和 JVM 的实现有所不同。在决定使用 ArrayList 或 LinkedList 之前,应该考虑实际应用场景和数据操作的特点。
42. 如何实现数组和 List 之间的转换?
在 Java 中,数组和 List(列表)之间的转换非常常见。下面分别介绍几种转换方法。
1)数组转 List
-
使用
Arrays.asList()
String[] array = {"a", "b", "c"}; List<String> list = Arrays.asList(array);
注意:这种方法得到的 List 是与原数组绑定在一起的,即对其中一个的修改会影响另一个。
-
使用
Collections.addAll()
String[] array = {"a", "b", "c"}; List<String> list = new ArrayList<>(); Collections.addAll(list, array);
这种方法得到的 List 是独立的。
2)List 转数组
-
使用
List.toArray()
List<String> list = Arrays.asList("a", "b", "c"); String[] array = (String[]) list.toArray();
注意:这种转换默认得到的数组类型是 Object 类型的,如果需要得到特定类型的数组,需要强制类型转换。
-
使用
List.toArray(T[] a)
List<String> list = Arrays.asList("a", "b", "c"); String[] array = list.toArray(new String[0]); // 传入一个类型匹配的、长度为 0 的数组
这种方法可以避免强制类型转换,并且可以指定数组类型。
总结:
Arrays.asList()
方法简单易用,但得到的 List 与原数组绑定,操作需谨慎。Collections.addAll()
方法得到的 List 是独立的,更加灵活。List.toArray()
方法需要强制类型转换,而List.toArray(T[] a)
可以避免这个问题。
领【150 道精选 Java 高频面试题】请go公众号:码路向前 。