数组、ArrayList底层实现原理、如何实现数组和List之间的转换、ArrayList和LinkedList的区别

数组

数组(Array) 是一种用连续的内存空间存储相同数据类型数据的线性数据结构。
在这里插入图片描述

为什么数组索引从0开始呢?假如从1开始不行吗?
1.在根据数组索引获取元素的时候,会用索引和寻址公式来计算内存所对应的元素数据,寻址公式是:数组的首地址+索引乘以存储数据的类型大小
2.如果数组的索引从1并始,寻址公式中,就需要增加一次减法操作,对于CPU来说就多了一次指令,性能不高。

查找的时间复杂度
随机(通过下标)查询的时间复杂度是O
查找元素(未知下标)的时间复杂度是O(n)
查找元素(未知下标但排序)通过二分查找的时间复杂度是O(logn)

插入和删除时间复杂度
插入和删除的时候,为了保证数组的内存连续性,需要挪动数组元素,平均时间复杂度为O(n)

ArrayList底层实现原理

ArrayList底层是用动态的数组实现的
ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10
ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组
ArrayList在添加数据的时候

1.确保数组已使用长度(size)加1之后足够存下下一个数据。
2.计算数组的容量,如果当前数组已使用长度+1后的大于当前的数组长度,则调用grow方法扩容(原来的1.5倍)。
3.确保新增的数据有地方存储之后,则将新元素添加到位于size的位置上。
4.返回添加成功布尔值。

如何实现数组和List之间的转换

//数组转List
public static void testArray2List(){
	String[]strs = {"aaa","bbb","ccc"};
	List<String> list = Arrays.asList(strs);
	for (String s : list) {
	System.out.println(s);
	}
}
//List转数组
public static void testList2Array(){
	List<String> list = new ArrayList<String>();
	list.add("aaa");
	list.add("bbb");
	list.add("ccc");
	String[]array = list.toArray(new String[list.size()]);
	for (String s : array){
		System.out.println(s);
	}
}

数组转List,使用JDK中java.util.Arrays工具类的asList方法
List转数组, 使用List的toArray方法。无参toArray方法返回Object数组,传入初始化长度的数组对象,返回该对象数组

用Arrays.asList转List后,如果修改了数组内容,list受影响吗?
List用toArray转数组后,如果修改了List内容,数组受影响吗?

1.Arrays.asList转换list之后,如果修改了数组的内容,list会受影响,因为它的底层使用的Arrays类中的一个内部类ArrayList来构造的集合,在这个集合的构造器中,把我们传入的这个集合进行了包装而已,最终指向的都是同一个内存地址
2.list用了toArray转数组后,如果修改了list内容,数组不会影响,当调用了toArray以后,在底层是它是进行了数组的拷贝,跟原来的元素就没啥关系了,所以即使list修改了以后,数组也不受影响

单向链表

1.链表中的每一个元素称之为结点(Node)
2.物理存储单元上,非连续、非顺序的存储结构
3.单向链表:每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。记录下个结点地址的指针叫作后继指针next

查询的时间复杂度:
1.只有在查询头节点的时候不需要遍历链表,时间复杂度是O(1)
2.查询其他结点需要遍历链表,时间复杂度是O(n)

增删操作的时间复杂度:
1.只有在添加和删除头节点的时候不需要遍历链表,时间复杂度是O(1)
2.添加或删除其他结点需要遍历链表找到对应节点后,才能完成新增或删除节点,时间复杂度是O(n)
在这里插入图片描述

ArrayList和LinkedList的区别

双向链表

1.每个结点不止有一个后继指针next 指向后面的结点
2.有一个前驱指针 prev指向前面的结点

查询的时间复杂度:
1.查询头尾结点的时间复杂度是O(1)
2.平均的查询时间复杂度是O(n)
3.给定节点找前驱节点的时间复杂度为O(1)

增删操作的时间复杂度:
1.头尾结点增删的时间复杂度为O(1)
2.其他部分结点增删的时间复杂度是O(n)
3.给定节点增删的时间复杂度为 O(1)
在这里插入图片描述

双链表对比单链表:
1.双向链表需要额外的两个空间来存储后继结点和前驱结点的地址
2.支持双向遍历,这样也带来了双向链表操作的灵活性
在这里插入图片描述

ArrayList和LinkedList的区别:

1.底层数据结构
ArrayList是动态数组的数据结构实现
LinkedList是双向链表的数据结构实现
2. 操作数据效率
ArrayList按照下标查询的时间复杂度O(1)【内存是连续的,根据寻址公式】
LinkedList不支持下标查询
查找(未知索引) : ArrayList需要遍历,链表也需要链表,时间复杂度都是O(n)
新增和删除
ArrayList 尾部 插入和删除,时间复杂度是O(1); 其他部分增删需要挪动数组,时间复杂度是O(n)
LinkedList 头尾节点 增删时间复杂度是O(1),其他都需要遍历链表,时间复杂度是O(n)
3.内存空间占用
ArrayList底层是数组,内存连续,节省内存
LinkedList是双向链表需要存储数据,和两个指针,更占用内存
4.线程安全
ArrayList和LinkedList都不是线程安全
如果需要保证线程安全,有两种方案:
(1)在方法内使用,局部变量则是线程安全的
(2)使用线程安全的ArrayList和LinkedList

  • 28
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ArrayListLinkedList都是Java中常见的List集合类型,它们的底层实现方式不同,因此它们的性能特点也不同。 ArrayList底层使用数组实现,因此它具有以下特点: 1. 随机访问效率高:由于ArrayList中的元素是按照顺序存储在数组中的,因此可以通过下标来快速访问任意一个元素,时间复杂度为O(1)。 2. 插入和删除效率低:由于ArrayList中的元素是存储在数组中的,因此在插入或删除元素时需要移动数组中的其他元素,时间复杂度为O(n)。 3. 空间浪费少:由于ArrayList中的元素是存储在数组中的,因此每个元素占用的空间是固定的,不会因为元素的增加而浪费空间。 LinkedList底层使用双向链表来实现,因此它具有以下特点: 1. 随机访问效率低:由于LinkedList中的元素是按照指针链接的方式存储在链表中的,因此不能通过下标来快速访问任意一个元素,需要遍历链表,时间复杂度为O(n)。 2. 插入和删除效率高:由于LinkedList中的元素是存储在链表中的,因此在插入或删除元素时只需要修改指针的指向,时间复杂度为O(1)。 3. 空间浪费多:由于LinkedList中的元素是存储在链表中的,每个元素需要额外的指针来链接前后元素,因此每个元素占用的空间比ArrayList大。 需要根据具体的场景来选择使用ArrayList还是LinkedList。如果需要快速访问元素,应该选择ArrayList;如果需要频繁插入或删除元素,应该选择LinkedList

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值