LinkedList 和 ArrayList 的全面对比讲解 📚:
1. 数据结构的不同🛠️:
-
LinkedList:它基于双向链表。每个元素(我们叫它节点)不仅存储数据,还存储了指向前后元素的两个指针🔗。这就像是一列火车🚂,每节车厢都知道自己前后是哪节车厢。
生活例子🍔:假如你正在排队买奶茶🥤。你前后都有一个人,想插队的话,你只需要拉住你前面和后面的人,让他们也拉住你。你可以很快插入队伍,因为你只需要跟两个人沟通(前后两个人)。同样,删除也是一样的简单😉。
-
ArrayList:它基于动态数组。这意味着它需要一段连续的内存来存储数据📦,而不是像LinkedList那样通过指针相互连接。每个元素都通过索引快速访问,就像一排整齐的盒子📦📦📦。
生活例子🍰:想象你在超市购物🛒,推着购物车走过货架📑。每个商品都有固定的编号🏷️(索引),你只需要找到相应编号的货架,就可以快速拿到商品。这是因为所有商品都整齐排列在货架上,不用一个个看过去。
2. 访问速度(随机访问)🏃♀️💨:
-
LinkedList:因为数据是通过链表存储的,要访问某个特定位置的元素,你需要从头(或尾)开始,逐个走过每个节点才能找到目标⚙️。这个过程就像翻找一个很长的链子,一环一环地往下走🔗,所以速度会很慢。
生活例子🍔:想象你有一串钥匙🔑,但你想要找到其中的第10把钥匙。你得从第1把开始一把一把数过去,直到找到第10把。这就是LinkedList的访问方式,比较费时⏳。
-
ArrayList:因为数组是连续存储的,所有元素的内存地址是连续的。所以,你可以直接通过索引来快速访问某个元素🏃♂️,就像有一份清单📑,上面标明了每个商品在超市里的具体位置。
生活例子🍰:还记得我们在超市找商品的例子吗?你只需要找到货架编号,就能直接快速拿到商品🏷️。这就是ArrayList访问的方式,速度非常快⚡。
3. 插入和删除操作的不同🔄:
-
LinkedList:因为它是链表结构,插入和删除操作非常方便,特别是在头部或尾部插入和删除元素的时候。你只需要调整前后节点的指针指向,几乎不会影响其他节点🛠️。然而,如果你想在中间位置进行操作,你还得先从头开始找这个位置⚙️。
生活例子🍔:比如你想在排队时插队,或者你排到一半突然不想买奶茶了🍹,离开队伍。因为你只需要拉住你前面和后面的人,告诉他们换一下顺序,所以插入和删除非常方便,效率高🏆。
-
ArrayList:在末尾插入是比较快的,因为只需要将新元素放在最后一个位置📦。但是如果你想在中间插入或删除元素,所有后面的元素都要往前或往后移位。这就像你在一排整齐的盒子里中途插入一个新盒子📦,你不得不移动很多其他盒子才能腾出空间。
生活例子🍰:想象你在超市的货架中间突然想加一个新商品,你必须把后面的商品全都挪开才能腾出空位😵。所以在中间插入和删除的成本比较高。
4. 内存占用情况📏:
-
LinkedList:由于每个节点除了存储数据外,还要存储指向前后节点的两个指针🧷,所以相比ArrayList,它占用的内存更多📊。
生活例子🍔:就像每个人手里都拿着两根绳子,一根拉住前面的人,一根拉住后面的人。每个人不仅要站着,还得多占点地方拉绳子,所以会比直接排好队的ArrayList占用更多空间🪢。
-
ArrayList:它只需要存储数据本身,没有额外的指针开销。虽然在增长时可能需要重新分配内存空间,但通常内存占用相对较少📉。
生活例子🍰:ArrayList就像是一个紧凑的货架,所有商品整齐排列,没有多余的“拉绳子”的动作,所以它更加节省空间💡。
5. 性能和使用场景💼:
- LinkedList:适用于需要频繁插入和删除元素的场景,尤其是在队列的头部和尾部操作时⏩。比如:当你需要模拟一列排队系统时👥,LinkedList非常合适。
- ArrayList:适用于快速访问数据的场景,尤其是频繁通过索引来读取数据的情况📑。如果你经常要遍历和访问元素,比如展示商品列表🛒,ArrayList会表现得更好。
总结大比拼🏆:
特性 | LinkedList | ArrayList |
---|---|---|
数据结构 | 基于双向链表,节点包含数据和指针🔗 | 基于动态数组,数据连续存储📦 |
随机访问速度 | 慢,需要遍历节点🔗 | 快,可以通过索引直接访问⚡ |
插入和删除 | 头尾操作快,尤其是插入/删除时效率高⏩ | 末尾操作快,但在中间插入/删除时需要移动大量数据📦📦📦 |
内存占用 | 占用内存较多,因为每个节点都有两个额外的指针🔗 | 占用内存较少,只存储数据本身📉 |
适用场景 | 适合需要频繁插入/删除的场景,如队列👥 | 适合需要快速访问数据的场景,如商品列表🛒 |
在 LinkedList 和 ArrayList 中插入元素的性能比较📝
这部分代码会对比在 LinkedList 和 ArrayList 中插入元素的效率。在这个例子中,我们向列表中插入 100000 个元素,看看它们的表现。
import java.util.ArrayList;
import java.util.LinkedList;
public class InsertComparison {
public static void main(String[] args) {
// 创建 LinkedList 和 ArrayList
LinkedList<String> linkedList = new LinkedList<>();
ArrayList<String> arrayList = new ArrayList<>();
// 插入 100000 个元素到 LinkedList
long linkedListStartTime = System.nanoTime(); // 记录开始时间
for (int i = 0; i < 100000; i++) {
linkedList.add("Element " + i); // 向 LinkedList 中添加元素
}
long linkedListEndTime = System.nanoTime(); // 记录结束时间
System.out.println("LinkedList 插入 100000 个元素花费的时间: " + (linkedListEndTime - linkedListStartTime) + " 纳秒");
// 插入 100000 个元素到 ArrayList
long arrayListStartTime = System.nanoTime(); // 记录开始时间
for (int i = 0; i < 100000; i++) {
arrayList.add("Element " + i); // 向 ArrayList 中添加元素
}
long arrayListEndTime = System.nanoTime(); // 记录结束时间
System.out.println("ArrayList 插入 100000 个元素花费的时间: " + (arrayListEndTime - arrayListStartTime) + " 纳秒");
}
}
解释💡:
- LinkedList 在插入时,特别是在尾部插入,表现会不错,因为它不需要移动其他元素,只是修改指针🔗。
- ArrayList 在插入时,尤其是在末尾插入,也表现得很好,但如果容量不足时,它需要重新扩展数组📦,所以在某些情况下可能会变慢。
2. 在 LinkedList 和 ArrayList 中访问元素的性能比较📝
这部分代码对比了 LinkedList 和 ArrayList 在访问元素时的性能。我们尝试获取第 50000 个元素,看看它们的表现如何。
import java.util.ArrayList;
import java.util.LinkedList;
public class AccessComparison {
public static void main(String[] args) {
// 创建并填充 LinkedList 和 ArrayList
LinkedList<String> linkedList = new LinkedList<>();
ArrayList<String> arrayList = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
linkedList.add("Element " + i);
arrayList.add("Element " + i);
}
// 访问 LinkedList 的第 50000 个元素
long linkedListStartTime = System.nanoTime();
String linkedListElement = linkedList.get(50000);
long linkedListEndTime = System.nanoTime();
System.out.println("LinkedList 访问第 50000 个元素花费的时间: " + (linkedListEndTime - linkedListStartTime) + " 纳秒");
// 访问 ArrayList 的第 50000 个元素
long arrayListStartTime = System.nanoTime();
String arrayListElement = arrayList.get(50000);
long arrayListEndTime = System.nanoTime();
System.out.println("ArrayList 访问第 50000 个元素花费的时间: " + (arrayListEndTime - arrayListStartTime) + " 纳秒");
}
}
解释💡:
- LinkedList 需要从头(或尾)开始逐个查找元素,因为它是链表结构🔗,所以访问速度相对慢一些⏳。
- ArrayList 可以通过索引直接访问元素📑,因此访问速度非常快⚡。
3. 在 LinkedList 和 ArrayList 中在开头插入元素的性能比较📝
这一部分代码对比了在 LinkedList 和 ArrayList 的头部插入操作。我们在开头插入一个新元素,看看性能差异。
import java.util.ArrayList;
import java.util.LinkedList;
public class HeadInsertComparison {
public static void main(String[] args) {
// 创建并填充 LinkedList 和 ArrayList
LinkedList<String> linkedList = new LinkedList<>();
ArrayList<String> arrayList = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
linkedList.add("Element " + i);
arrayList.add("Element " + i);
}
// 在 LinkedList 的开头插入元素
long linkedListStartTime = System.nanoTime();
linkedList.add(0, "New Element");
long linkedListEndTime = System.nanoTime();
System.out.println("LinkedList 在开头插入元素花费的时间: " + (linkedListEndTime - linkedListStartTime) + " 纳秒");
// 在 ArrayList 的开头插入元素
long arrayListStartTime = System.nanoTime();
arrayList.add(0, "New Element");
long arrayListEndTime = System.nanoTime();
System.out.println("ArrayList 在开头插入元素花费的时间: " + (arrayListEndTime - arrayListStartTime) + " 纳秒");
}
}
解释💡:
- LinkedList 只需要调整头部节点的指针🔗,所以在头部插入元素非常快⏩。
- ArrayList 则需要移动所有元素📦,所以在头部插入元素时会很慢😵。
总结大比拼🏆:
- 插入性能:LinkedList 在头尾插入时表现优异,而 ArrayList 只在末尾插入时表现好。
- 访问性能:ArrayList 的访问速度比 LinkedList 快很多,尤其是在随机访问的情况下。
- 内存占用:LinkedList 由于存储了前后两个指针,占用更多内存;ArrayList 内存占用较少,但可能需要扩展数组。