JDK源码学习之Arraylist与LinkedList

ArrayList和LinkedList是我们在开发过程中常用的两种集合类,本文将从底层源码实现对其进行简单介绍。

下图是Java集合类所涉及的类图。

JDK源码学习之Arraylist与LinkedList

一.ArrayList

从上面的集合类图可以看出,ArrayList实现了List接口。ArrayList是顺序的集合容器,容器中可以存放null元素,而底层则是通过一个可自动增长大小的动态数组实现的。ArrayList不是线程安全的,也没有实现同步。

1.1 ArrayList操作性能

访问数组中第 n 个数据的时间花费是 O(1),但是要在数组中查找一个指定的数据则是 O(N)。当向数组中插入或者删除数据的时候,最好的情况是在数组的末尾进行操作,时间复杂度是 O(1),但是最坏情况是插入或者删除第一个数据,时间复杂度是O(N)。在数组的任意位置插入或者删除数据的时候,后面的数据全部需要移动,移动的数据还是和数据个数有关所以总体的时间复杂度仍然是O(N)。

JDK源码学习之Arraylist与LinkedList

1.2 私有属性

ArrayList有两个私有属性,一个是实现数据存储的数组,一个是表示数组中元素的个数。值得注意的是关键字transient。

JDK源码学习之Arraylist与LinkedList

ArrayList这个类实现了Serializable接口。Java的Serializable提供了一种持久化对象实例的机制,当持久化对象时,可能某个特殊的对象数据成员我们不想让其用Serializable机制保存它,可以使用关键字transient来进行屏蔽。此外还有个保护的属性:modCount,含义为已从结构上修改此列表的次数。从结构上修改是指更改列表的大小,或者打乱列表,从而使正在进行的迭代产生错误的结果。

1.3 构造方法

ArrayList中有三个构造函数,一个是默认构造一个容量为10的数组,一个指定容量的空列表和一个Collection类型的空列表。我们可以在使用时通过指定我们预估到的数组容量,来减少扩容次数

1.4 数组扩容

每一个ArrayList的实例都有一个容量,用来表示存储数据的数组大小。容器内的元素不能大于当前当前的容器大小。当向容器中添加数据时,若容器的容量不足,容器会自动扩容。通过对比jdk1.7的ArrayList源码发现,扩容两个方法是不一样的,jdk1.6中使用的是除法对其容量进行计算(加0.5倍),而jdk1.7中则使用的是移运算。

JDK源码学习之Arraylist与LinkedList

位运算是CPU直接操作的,除法等四则运算都是基于移位运算的,所以当有大量计算的时候,移位运算可以大大节约CPU的时间。通过1千万次位运算和四则运算做对比数据结果如下:

JDK源码学习之Arraylist与LinkedList

可以看出,在计算大量数据的情况下,移位运算的花费时间比乘除法快很多。

1.5 数组复制方法

JDK源码学习之Arraylist与LinkedList

可以看到在ensureCapacity(intminCapacity)中调用的是Arrays的copyOf()方法,而在add方法中,调用的数组复制方法为:

native void arraycopy();Arrays的copyOf方法的实现:

JDK源码学习之Arraylist与LinkedList

实现copyOf的时候会新创建一个大小为newCapcity的数组,然后将旧的elementData放入其中。

1.6 小节

1、通过查看ArrayList的源码,注意到有三个不同的构造方法,合理使用构造方法能减少数组扩容拷贝造成的额外开销。

2、ArrayList大量调用了Arrays.copyOf和System.arrayCopy的方法,注意这两个方法的区别。

3、jdk1.6和1.7中数组扩容的方法不一致,注意比较有差异的地方。

二.LinkedList

LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现。基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些。

除了实现 List 接口外,LinkedList类还实现 Deque接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

LinkedList定义:

public class LinkedList<E > extends AbstractSequentialList<E>

implements List<E>,Deque<E>, Cloneable, java.io.Serializable

2.1 底层数据结构

与ArrayList的区别在于,LinkedList底层是基于双向链表实现的:

以上的数据结构可以从LinkedList的私有属性看出:

private transient Entry<E> header = newEntry<E>(null, null, null);//头结点是不存放元素的

private transient int size = 0;//双向循环链表的大小

2.2 双向循环列表的操作性能

对于双向循环链表的插入和删除操作只是多移动几个指针。

备注:这里只是单纯的描述双向链表这种数据结构的插入和删除性能,下文将对比ArrayList与LinkedList的性能。

2.3 构造函数

LinkedList类有两个构造函数,一个是无参数的,一个是构造任意类型的集合类的列表

该构造函数构造一个空的列表,header头结点表示如下, 形成一个闭环。

有参构造方法,参数为collection的c,this()调用默认的无参构造函数,然后再调用addAll()方法,将c中的元素添加加入列表。

三.LinkedList与ArrayList比较

1.ArrayList是基于数组的数据结构,LinkedList是基于链表的数据结构。

2.ArrayList内部的元素可以直接通过get与set方法进行访问,因为ArrayList本质上就是一个数组.但LinkedList在get与set方面弱于ArrayList.

当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比,如果数据和运算量很小,那么对比将失去意义.

3.此外 LinkedList 还实现了 Queue 接口,该接口比List提供了更多的方法,包括 offer(),peek(),poll()等.

注意: 默认情况下ArrayList的初始容量非常小,所以如果可以预估数据量的话,分配一个较大的初始值属于最佳实践,这样可以减少调整大小的开销。

4.对于ArrayList与LinkedList性能下图做个简单比较

* 表中的 add() 代表 add(E e),而 remove()代表 remove(int index)

ArrayList 对于随机位置的add/remove,时间复杂度为 O(n),但是对于列表末尾的添加/删除操作,时间复杂度是 O(1). 而LinkedList对于随机位置的add/remove,时间复杂度为 O(n),但是对于列表 末尾/开头 的添加/删除操作,时间复杂度是 O(1).

下面是测试代码:

JDK源码学习之Arraylist与LinkedList

输出结果截图如下:

JDK源码学习之Arraylist与LinkedList

上述测试可以看出LinkedList在 进行add和remove操作时更快,而在进行get操作时较慢.

通过本次源码学习之旅,我从LinkedList、ArrayList的底层结构出发,更深刻地了解了这两个类的一些基本操作方法,文章中有不周全的地方欢迎指正,共同学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/456999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习记录4

学习了python基本数据类型&#xff0c;附学习笔记图及操作图 转载于:https://www.cnblogs.com/bgd140206127/p/6549229.html

self 实例对象-代码详细解释

self代表类的实例&#xff0c;而非类哪个对象调用方法&#xff0c;那么该方法中的self就代表那个对象self.__calss__ 代表类名class Person(object):def run(self):print("run")print(self.__class__)p self.__class__("tt",30,10,20)print(p)def eat(sel…

CString之GetBuffer与ReleaseBuffer

我们知道&#xff0c;CString是MFC中提供的方便字符串操作的一个类&#xff0c;非常好使&#xff0c;具有自动动态内存管理功能。 GetBuffer()主要作用是将字符串的缓冲区长度锁定&#xff1b; ReleaseBuffer()则是解除对缓冲区的锁定&#xff0c;这样使得CString对象在以后的代…

mac 编译android系统,mac 编译 Android 系统杂记

挂载android分区sudo hdiutil attach ~/android_code/android7.dmg.sparseimage -mountpoint /Volumes/android原放入U盘&#xff1a;echo 188jinghao | sudo -S hdiutil attach ~/android7.dmg.sparseimage -mountpoint /Volumes/android放入机械硬盘sudo hdiutil attach /Vol…

Java开发必须熟悉的Linux命令总结

身为一个Java开发人员&#xff0c;这些常用的Linux命令必须掌握。即使平时开发过程中没有使用Linux&#xff08;Unix&#xff09;或者mac系统&#xff0c;也需要熟练掌握Linux命令。因为很多服务器上都是Linux系统。所以&#xff0c;要和服务器机器交互&#xff0c;就要通过she…

构析函数

析构函数&#xff1a;__del__() 释放对象时自动调用 class Person(object):def run(self):print("run")def eat(self,food):print("eat"food)def __init__(self,name,age,height,weight):self.name nameself.height heightself.age ageself.weight …

Java 序列化Serializable详解(附详细例子)

Java 序列化Serializable详解&#xff08;附详细例子&#xff09; 1、什么是序列化和反序列化Serialization&#xff08;序列化&#xff09;是一种将对象以一连串的字节描述的过程&#xff1b;反序列化deserialization是一种将这些字节重建成一个对象的过程。 2、什么情况下需要…

kettle-实现每个分组的前N的数据

2019独角兽企业重金招聘Python工程师标准>>> 第一步&#xff1a;创建表及数据&#xff1a; create table uid(uid int, --uidcate varchar(20), --类别price double --金额 ) insert into uid values(123,c1,21); insert into uid values(123,c2,23); insert into u…

重写__repr__与__str__函数

重写&#xff1a;将函数重新定义写一遍__str__():再调用print 打印对象时自动调用&#xff0c;是给用户用的是一个描述对象的方法__repr__():是给机器用的&#xff0c;在python解释器里面直接敲对象名再回车调用的方法注意&#xff1a;在没有str时&#xff0c;且有repr,str re…

linux nexus 使用问题

2019独角兽企业重金招聘Python工程师标准>>> 问题一&#xff0c;启动提示设置RUN_AS_USERroot 但是&#xff0c;设置export或 /etc/profile未生效。 **************************************** WARNING - NOT RECOMMENDED TO RUN AS ROOT *************************…

项目回顾-PopupWindow

右上菜单&#xff0c;可以通过 重写 onCreateOptionsMenu指定 menu&#xff0c; 重写 onOptionsItemSelected 来响应点击事件 不过 这个菜单在某些手机上弹出的有点卡顿&#xff0c;而且如果不对主题进行设置&#xff0c;会从actionbar 上直接弹出&#xff0c;而不是下面 如果想…

android listpreference 自定义,Android ListPreference的用法一

xmlns:android"http://schemas.android.com/apk/res/android"android:key"screen_list"android:title"标题"android:summary"说明摘要">< ListPreferenceandroid:key"myListPreference"android:title"标题"…

C语言求最大公约数和最小公倍数的几种算法

求最小公倍数算法&#xff1a; 最小公倍数两整数的乘积最大公约数 求最大公约数算法&#xff1a; (1)辗转相除法 有两整数a和b&#xff1a; ① a%b得余数c ② 若c0&#xff0c;则b即为两数的最大公约数 ③ 若c≠0&#xff0c;则ab&#xff0c;bc&#xff0c;再回去执行①…

3月15日云栖精选夜读:双管齐下,MaxCompute数据上云与生态

双管齐下&#xff0c;MaxCompute数据上云与生态 作者&#xff1a;场景研读 Go语言并发机制初探 作者&#xff1a;邴越 趣拍云短视频SDK全面升级&#xff0c;简单易用引开发者点赞 作者&#xff1a;sherry是雪梨 发表在&#xff1a;趣拍云团队 阿里云机器学习平台编程模型演…

qt android glsl,基于Qt的OpenGL学习(1)—— Hello Triangle

简介要学习OpenGL的话&#xff0c;强烈安利这个教程JoeyDeVries的learnopengl&#xff0c;这里是中文翻译好的版本。教程中使用OpenGL是通过GLFW这个库&#xff0c;而在Qt中对OpenGL封装得很好&#xff0c;并且和GUI以及IO相关的处理Qt更便捷&#xff0c;学习起来更轻松。这里就…

解决:Not Found: /favicon.ico

直接说解决办法&#xff1a; &#xff08;1&#xff09;制作一个 favicon.ico图标放在<head></head>标签中 <link rel"shortcut icon" href"xxxxxxxxxx.ico" type"image/x-icon" /> <!--制作的图标&#xff0c;使用hr…

多态方法调用的解析和分派

方法调用并不等同于方法执行&#xff0c;方法调用阶段唯一的任务就是确定被调用方法的版本&#xff08;即调用哪一个方法&#xff09;&#xff0c;暂时还不涉及方法内部的具体运行过程。在程序运行时&#xff0c;进行方法调用是最普遍、最频繁的操作&#xff0c;Class文件的编译…

ES6:Set和Map

Set Set:类似数组&#xff0c;但是成员的值都是唯一的&#xff0c;没有重复。Set本身是一个构造函数&#xff0c;用来生成Set数据结构。他包含的方法&#xff1a;add: 添加某个值&#xff0c;返回Set结构本身。delete: 删除某个值&#xff0c;返回一个布尔值&#xff0c;表示是…

九九乘法表[循环嵌套]

#九九乘法表 # 1*11 # 1*22 2*24 # 1*33 2*36 3*39 # ...#循环嵌套 #行数 i 1 while i < 9:# 打印每行的内容j 1while j < i:print("%d * %d %3d " % (i, j, i * j), end)j 1print() # 换行i 1while嵌套&#xff1a;w 1 while w < 10: #外层循…

关于用VS写C程序运行时出现烫字以及乱码的问题的原因

最近在复习C语言写程序时&#xff0c;突然碰到标题上的这种情况&#xff0c;后来经过上网查找以及逐步调试才发现原来是在打印数组的时候“越界”导致的&#xff0c;因为程序在默认初始化char类型的数组时&#xff0c;初始化的值是“烫”字&#xff0c;一般情况下是字符串未初始…