Java基础 集合类回顾------list列表类

java集合类

集合类存放于java.util包中。

集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。

集合类型主要有3种:set(集)、list(列表)和map(映射)。

集合接口分为:Collection和Map,list、set实现了Collection接口

(来自百度百科-Java集合类

这里我们谈一谈list。 

list表示的是列表,可以存储一组有重复对象的元素,且能维护元素之间的顺序。

根据上图我们可以看到list下我们常用的有VectorArrayListLinkedList

它们各自的类继承结构如下:

Collection<--List<--Vector<--Stack

Collection<--List<--ArrayList

Collection<--List<--LinkedList

Vector、ArrayList、LinkedList简单的异同:

异:

1. Vector: 内部是通过数组实现的,且它是支持线程同步,但实现同步需要花费很高的代价,因此,访问它比访问ArrayList慢如果不考虑到线程的安全因素,一般用Arraylist效率比较高。

2. ArrayList:同Vector一样都是通过通过数组实现,但底层对操作没有加锁

3. LinkedList双向链表结构存储数据的,适合数据的动态插入和删除,随机访问和遍历速度比较慢。还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

同:

1. Vector和ArrayList:由于二者都是基于数组实现的,所以不是从最后插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。

2. LinkedList和ArrayList:底层都没有对操作加锁,都是线程不安全的!

 

下面会对不同点进行具体分析。

 

Vector和ArrayList的区别(基于JDK1.8):


1. 虽然Vector和ArrayList底层都是Object数组,但它们的关键字却不一样

可以看到在ArrayList中,Object[]的关键字是transient的,而在Vector中只是protected

那ArrayList的elementData数据不会在序列化后丢失吗?

被transient修饰的变量在序列化时将不会被序列化。 

答案当然是不会。elementData之所以要设计为用transient修饰,是因为ArrayList内部有自动扩容机制,每当最小所需的空间大于elementData的长度时就会进行扩容,而且每次扩容为原来的1.5倍。对扩容机制不了解的可以参考ArrayList的扩容机制这篇博客。

因此有时候ArrayList的Object数组会存在一些未使用的区域,这些区域是不必序列化的,而且ArrayList中自己实现了write/readObject方法,仅仅去序列化有效的数据。

但至于为什么Vector的elementData不用transient修饰目前还不知道,希望知道的朋友能为我解惑 ( p_q)


2. 扩容的大小不一样

ArrayList每次扩容为原来的1.5倍

Vector的扩容可以由用户在构造器中赋予capacityIncrement,没有给的话默认为0,这样就是每次扩容为原来的两倍

注意它们的扩容都是调用的Arrays.copyOf()函数,对这个不清楚的可以看看Arrays.copyOf()方法详解

总之就是会重新申请一个长度为newCapacity的数组,然后再把旧的数据拷贝过去,之前旧的数组则由会被jvm的垃圾收集器回收,在数据量很大的时候还是很耗资源的,所以最好还是避免它们频繁的自动扩容。


3. elementData的初始化大小默认值不同

可以发现ArrayList初始化大小是为空的Object数组,然后在你第一次往里面添加元素时会扩容到DEFAULT_CAPACTIY(为10)

Vector则是初始化为大小为10的Object[]数组


4. 对多线程的支持不一

Vector中的方法都是synchronized线程同步的,所以可以在多线程的场景下使用;

ArrayList中没有对操作加锁,所以是线程不同步的

 

ArrayList和LinkedList的区别:


1. 底层的数据结构不同

由上面的分析已经可以知道ArrayList底层的数据结构是Object数组

从上面我们可以看出LinkedList的底层数据结构是双向链表(在JDK 1.6之前是循环双向链表),LinkedList中通过first指针last指针(也不知道叫指针恰不恰当,但感觉指针最好描述它)得到其首尾节点

由于它们的底层数据结构的不同,所以在功能上就会有各自更擅长的方面,数组的话对于指定位置的查询可以做到O(1),而链表则只能一个个遍历,所以时间复杂度是O(n);但对于增加和删除操作,如果不是从末尾改变的话,如果链表在 i 位置操作,则需要将后面 n - i 个元素往前移动,所以时间复杂度为 O(n - i),而链表则可以通过改变next和prev的指向做到近似O(1)的时间复杂度

所以当我们遍历LinkedList的时候需要特别注意!如果用for循环通过LinkedList的get()来取数据的话:

可以看到get()底层是通过for循环遍历获得数据的,这样的效率非常低,每次都要从头开始遍历一遍,所以最好要用iterator遍历(或者foreach,但foreach底层也是通过iterator实现的)

iterator是集合类帮我们封装的一个统一的遍历接口,因为集合类中有ArrayList、LinkedList、Map等多种数据结构,对他们的遍历方法都是不同的,所以为了让容器的使用者能够不用考虑遍历的实现问题,所以统一一个遍历接口以供容器使用者使用。


2. ArrayList实现了RandomAccess接口,而LinkedList没有

但是点进去可以发现RandomAccess什么也没有

我认为这个就像是Serializable接口一样,起到一个标识的作用

转载来自JavaGuide

所以说这个只是一个标志,用来标识是否支持随机访问功能。


3. 占用空间的不同

ArrayList的底层为Object数组,且由于扩容机制,一般后面都会有预留一些空间

LinkedList则是为了维护一条链表,需要在每个节点加上指向前驱和后继节点的指针

 

参考:

JavaGuide------极力推荐看!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值