本文主要讲解Java List集合
List
按添加顺序排列的集合,予许元素重复,且可以为 null。
主要说下List
下的两大常用子类ArrayList
,LinkedList
。
ArrayList
底层基于一个数组实现,数组长度可动态扩容。数组默认长度 10。‘
LinkedList
底层基于链表实现,
不需要扩容。
ArrayList
跟 LinkedList
有什么区别呢?
增删查改的区别就不说了。分析下内存占用方面。
首先 先了解下一个对象实例占用多少内存。以64位压缩后的情况
对象占用内存都是8的倍数。
- 对象头 12字节。
- 基本数据类型成员变量占用字节,例如 byte(1字节) int(4字节)。
- 数组,对象,都是属于引用类型,占4字节。
- 对齐填充字节。
以简单点来说:就以这两大List集合都有的成员属性(size,元素) 来讲解内存占用情况。
都是以泛型Integer
为例。
ArrayList 内存占用
占用内存 对象头(12字节) + elementData(4字节) + size(4字节) + modCount (4字节) = 24字节,刚刚好是8的倍数。这仅仅是一个ArrayList实例的占用内存,存储的元素并未计算。
public class ArrayList {
/**
* 数组引用 占用 4字节
*/
transient Object[] elementData;
/**
* int类型 占用 4字节
*/
private int size;
protected transient int modCount = 0; // 4字节
}
一个Integer
的占用内存是:对象头(12字节) + value(4字节) = 16字节。
public class Integer {
// 4 字节
private final int value;
}
由于泛型是Integer
,所以 elementData = new Integer[10]。10是默认值
。
那么占用的内存是 10 * 16 = 160字节。
由此可计算出 一个泛型Integer
的ArrayList
10个容量的实例占用内存是 24字节 + 160字节 = 184字节。
LinkedList 内存占用
占用内存 对象头(12字节) + size(4字节) + first(4字节) + last(4字节) + modCount (4字节) = 28字节。由于28不是8的倍数 对齐填充4字节 = 32字节。
这仅仅是一个LinkedList实例的占用内存,存储的元素并未计算。
public class LinkedList {
// 4 字节
transient int size = 0;
// 4 字节
transient Node<E> first;
// 4 字节
transient Node<E> last;
// 4 字节
protected transient int modCount = 0;
}
一个Node实例占用内存:对象头(12字节) + item(4字节) + next(4字节) + prev(4字节) = 24字节。
private static class Node<Integer> {
// 4 字节
E item;
// 4 字节
Node<E> next;
// 4字节
Node<E> prev;
}
因为LinkedList
每添加一个元素,就需要创建1个Node
实例,所以一个泛型Integer
元素占用 24字节 + 16字节 = 40字节。10个元素就是 10 * 40 = 400字节
由此可计算出 一个泛型Integer
的LinkedList
10个元素的实例占用内存是 24字节 + 400字节 = 424字节。
得出结论:以下是最少占用字节数,因为没有考虑其他成员属性。
- ArrayList 泛型为 Integer,10个元素的内容占用为 184字节。
- LinkedList泛型为 Integer,10个元素的内容占用为 424字节。
为什么深究于内存占用?
- GC是在堆内存快不足的时候工作,GC工作的时候暂停所有用户线程。
- 所以在编码过程中,应当减少内存的销耗,也就减少了GC。
- 虽然占用内存不多,但苍蝇再小也是肉哇。
既然 ArrayList 的内存占用比 LinkedList内存少,那开发的时候就用ArrayList 咯? 这是错误的,不然也不会开发 LinkedList了,那这两个List集合什么场景下用?
ArrayList 应用场景
- 在已知容量大小的情况下,例如JDBC查询数据。
- 仅做查询。
- 没有 add操作,仅有少量的 remove 操作。(这不会触发扩容,数组对象也就不会更换)
- 可能存在 add 操作,或仅有一丢丢的 add 操作。
LinkedList 应用场景
- 针对于不知道容量大小,例如缓存临时数据。
- 数据操作频繁, add,remove。
在编码中,我们应该要保持活性,有时候可以选择的是时间换空间,也可以是空间换时间。
但是我们要时刻注意着写出来的代码是否占用了过多内存,且是否会导致GC。
纯属个人理解,如有不同意见,请勿采纳。