文章目录
Java集合类型有三种:set(集)、list(列表)和map(映射),而List集合是很常用的一种集合类型,
List
我们的常用用法:
List<Object> arrays = new ArrayList<>();
首先要清楚List是个接口,是Collection接口的子接口,拥有Collection所有方法外,还有一些对索引操作的方法。List集合是一个元素有序(每个元素都有对应的顺序索引,第一个元素索引为0)、且可重复的集合。
具体概念就不延申了。
ArrayList
ArrayList是个什么东西?
ArrayList是list的实现类,看下其构造函数:
transient Object[] elementData; // non-private to simplify nested class access
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
可以看到:ArrayList的最底层实现其实就是一个数组,而在Java中数组的长度是固定的,但是我们实际使用ArrayList的时候不难发现,无论你怎么往这个集合中添加元素,他都不会显示越界,这是为什么呢?可以查看下add方法:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
具体可以看下ensureCapacityInternal方法里面,最终可以跟踪到grow()方法,具体逻辑不展开了,总归就是ArrayList这个数组可以扩容,ArrayList相当于动态数组。
- 它继承于AbstractList,实现了List, RandomAccess(随机访问), Cloneable(克隆), java.io.Serializable(可序列化)这些接口。
- 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
- 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
- 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
LinkedList
他又是个啥子东西?
与ArrayList一样都是list接口的实现类,继承自AbstractSequentialList,而AbstractSequentialList继承自AbstractList,所以跟ArrayList会有共性。
LinkedList 内部使用的双向链表来存储元素。
ArrayList与LinkedList的区别
- ArrayList 和 LinkedList 是 List 接口的两种不同实现,并且两者都不是线程安全的。
- ArrayList 内部使用的动态数组来存储元素,LinkedList 内部使用的双向链表来存储元素,这也是 ArrayList 和 LinkedList 最本质的区别。由于内部使用的存储方式不同,导致它们的各种方法具有不同的时间复杂度。
- ArrayList 和 LinkedList 在内存的使用上也有所不同。LinkedList 的每个元素都有更多开销,因为要存储上一个和下一个元素的地址。ArrayList 没有这样的开销。
- ArrayList 占用的内存在声明的时候就已经确定了(默认大小为 10),不管实际上是否添加了元素,因为复杂对象的数组会通过 null 来填充。LinkedList 在声明的时候不需要指定大小,元素增加或者删除时大小随之改变(双向链表决定的)。LinkedList 允许内存进行动态分配,这就意味着内存分配是由编译器在运行时完成的,我们无需在 LinkedList 声明的时候指定大小。。
- ArrayList 只能用作列表;LinkedList 可以用作列表或者队列,因为它还实现了 Deque 接口。
- 查询的时候,ArrayList 比 LinkedList 快。插入删除的时候LinkedList会更快些。因为数组的元素需要连续的内存位置来存储其值。这就是 ArrayList 进行删除或者插入元素的时候成本很高的真正原因,因为我们必须移动某些元素为新的元素留出空间,比如说:现在有一个数组,10、12、15、20、4、5、100,如果需要在 12 的位置上插入一个值为 99 的元素,就必须得把 12 以后的元素往后移动,为 99 这个元素腾出位置。LinkedList 不需要在连续的位置上存储元素,因为节点可以通过引用指定下一个节点或者前一个节点。也就是说,LinkedList 在插入和删除元素的时候代价很低,因为不需要移动其他元素,只需要更新前一个节点和后一个节点的引用地址即可。。
个人觉得,不知道用哪个,除非有队列使用需求,其它一律使用ArrayList即可。
快速构建list集合
最常见的就是new ArrayList了:
List<String> stringList = new ArrayList<>();
stringList.add("apple");
stringList.add("watermelon");
stringList.add("banana");
这种肯定没毛病,但是比较繁琐,接下来列举几种快速构建的方式:
Collections.singletonList
Collections是集合框架的工具类,里面包含一些对集合的排序,搜索以及序列化的操作,内部的方法都是静态的,没有构造方法,不能实例化,要调用内部方法直接用类名调用(Collections.sort),并且Collections类服务于Collection框架。
List<String> abc = Collections.singletonList("abc");
可以看下singletonList方法:
可以看到:
- 调用Collections.singletonList(T o)方法其实是创建了一个SingletonList对象,SingletonList继承了抽象类AbstractList。
- SingletonList整个集合中只能有一个元素,从名字也能看出来,这个比较适合构建单元素的List集合。
- 可以看到element为final类型,所以这个唯一的元素一旦被初始化完成后就没法修改了。
- 不可以使用add方法,虽然包含add方法,但是执行的时候会报错:
Arrays.asList
Arrays位于java.util包下(Arrays是一个操作数组的工具类),该类里的方法都是静态方法可以通过Arrays.方法名()直接调用,其实跟Collections差不多,都是工具类。
List<String> stringList = Arrays.asList("abc", "def");
可以看下asList方法:
可以看到:
- asList(T... a)方法其实是创建了一个 ArrayList 对象,ArrayList 继承了抽象类 AbstractList。
- 允许有多个元素存入数组中,但是一旦初始化后,数组的长度就不可以再进行任何的更改,但是数组中的元素可以修改。
- asList方法中不允许传入null。
- ArrayList 中存储数组是final类型,所以一旦初始化完成后不可更改长度,可以遍历修改元素但是不可以增加及删除,否则会报UnsupportedOperationException错误。
ImmutableList.of
Guava是来自Google的Java库合集,包含了很多新的集合类型(比如multinmap和mulitset),不可变集合,图库和并发、I/O、哈希、缓存、原始类型等类型的工具集。
List<String> immutableList = ImmutableList.of("english", "chinese");
ImmutableList<String> list = ImmutableList.of("english", "chinese");
接下来我们看下ImuutableList.of的源码:
- 我们看下checkElementsNotNull方法:
可以看到其实就是判断元素是否为null,如果为null的话字节抛出空指针异常。所以说ImmutableList.of元素不能为null。 - 继续看下asImmutableList方法:
可以看到根据元素长度执行了三个逻辑:
长度为0:
public static <E> ImmutableList<E> of() {
return (ImmutableList<E>) EMPTY;
}
长度为1:
构造了一个SingletonImmutableList,继承于ImmutableList,单元素,final修饰不可变。
其它情况:
构造了一个RegularImmutableList,继承于ImmutableList,final修饰不可变,不可增删改。