Connor学Java - ArrayList

2023-10-19 00:20
文章标签 java arraylist connor

本文主要是介绍Connor学Java - ArrayList,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

Learn && Live

虚度年华浮萍于世,勤学善思至死不渝

前言

Hey,欢迎阅读Connor学Java系列,这个系列记录了我的Java基础知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/pSqcY,话不多说我们马上开始!

1.Array和ArrayList的区别?什么时候更适合用Array?

(1)Array可容纳基本数据类型和对象,而ArrayList只能容纳对象

(2)Array大小是固定的,必须在声明时指定,而ArrayList大小是可变的

什么时候更适合用Array

(1)列表的大小确定,大部分情况下仅存储和遍历数据

(2)存储基本数据类型,相比集合需要自动装箱,节省性能

(3)多维数组

2.ArrayList实现RandomAccess接口有何作用?为何LinkedList没实现这个接口

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializablepublic class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) {if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)return Collections.indexedBinarySearch(list, key);elsereturn Collections.iteratorBinarySearch(list, key);
}private static <T> int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {int low = 0;int high = list.size()-1;while (low <= high) {int mid = (low + high) >>> 1;Comparable<? super T> midVal = list.get(mid);int cmp = midVal.compareTo(key);if (cmp < 0)low = mid + 1;else if (cmp > 0)high = mid - 1;elsereturn mid; // key found}return -(low + 1);  // key not found
}private static <T> int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key) {int low = 0;int high = list.size()-1;ListIterator<? extends Comparable<? super T>> i = list.listIterator();while (low <= high) {int mid = (low + high) >>> 1;Comparable<? super T> midVal = get(i, mid);int cmp = midVal.compareTo(key);if (cmp < 0)low = mid + 1;else if (cmp > 0)high = mid - 1;elsereturn mid; // key found}return -(low + 1);  // key not found
}

RandomAccess接口只是一个标志接口,只要List集合实现这个接口,就能支持快速随机访问

通过查看Collections类中的binarySearch方法,可以看出,判断List是否实现RandomAccess接口来分别调用indexedBinarySearch(list, key)或IteratorBinarySearch(list, key),实现Random接口的List集合采用for循环遍历,而未实现这个接口的则采用迭代器遍历,即ArrayList一般采用for循环遍历,而LinkedList一般采用迭代器遍历

因此我们在实际做项目时,应该考虑到List集合的不同子类应采用不同的遍历方式,从而提高性能

3.ArrayList和LinkedList的区别?

ArrayList源码

// JDK7
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {private static final long serialVersionUID = 8683452581122892189L;private static final int DEFAULT_CAPACITY = 10;private static final Object[] EMPTY_ELEMENTDATA = {};// Object[]数组实现private transient Object[] elementData;private int size;// 默认大小为10public ArrayList(int initialCapacity) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);this.elementData = new Object[initialCapacity];}// 空ArrayListpublic ArrayList() {super();this.elementData = EMPTY_ELEMENTDATA;}...
}

LinkedLIst源码

public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable {transient int size = 0;// 链表表项private static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}/*** 链表开头* Invariant: (first == null && last == null) ||*            (first.prev == null && first.item != null)*/transient Node<E> first;/*** 链表结尾* Invariant: (first == null && last == null) ||*            (last.next == null && last.item != null)*/transient Node<E> last;/*** 添加表项,插入到链表的尾部**/public boolean add(E e) {linkLast(e);return true;}...
}

区别

(1)ArrayList和LinkedList的差别主要来自于两者采用的数据结构不同。

  • ArrayList是基于数组实现的,默认初始化大小为10,大小不足时会调用grow方法扩容
  • LinkedList是基于双向链表实现的,节点Node包含Item、prev、next,整个链表包含first、last,分别指向链表的头部、尾部

(2)ArrayList和LinkedList实现的接口不同。

  • ArrayList实现了RandomAccess接口,支持快速随机访问,使用for循环遍历
  • 而LinkedList没有实现,使用迭代器遍历。LinkedList实现了Deque接口,因此LinkedList可以作为双向队列、栈和List集合使用

(3)ArrayList和LinkedList增删查效率不同。

  • ArrayList是基于索引的数据结构,使用索引在数组中搜索速度很快,可以直接返回指定index处的元素。但插入、删除数据的开销是很大的,因为这需要移动和复制原数组
  • LinkedList在搜索时需要逐个遍历找到相应的位置,再返回内容。但在插入、删除数据时,不需要像ArrayList一样,不需要改变数组的大小,不需要移动和复制原数组

(4)ArrayList和LinkedList所需内存大小不同。

  • ArrayList的每个索引的位置是实际的数据,所需内存较小
  • LinkedList中的每个节点中存储的包含实际的数据、前驱和后驱,需要更多的内存

ArrayList的增删未必比LinkedList慢

(1)如果增删都在末尾操作,每次调用remove()和add(),此时ArrayList不需要移动和复制数组,如果数据量有百万级时,速度比LinkedList要快

(2)如果删除操作的位置在中间。由于LinkedList的消耗主要是在遍历上,ArrayList的消耗主要是在移动和复制上(arrayCopy),而LinkedList的遍历速度要慢于ArrayList的复制移动速度。故数据量在百万级时,还是ArrayList较快

补充:transient关键词的作用

被transient修饰的变量不参与序列化和反序列化。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。

4.ArrayList的扩容机制?

先看源码:

public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}private void ensureCapacityInternal(int minCapacity) {if (elementData == EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);
}private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;// (1)int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);
}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();// return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

(1)当使用add方法时首先调用ensureCapacityInternal方法,传入当前大小size + 1,检查是否需要扩容

(2)注释(1)处,newCapacity = oldCapacity + (oldCapacity >> 1),扩容为原大小的1.5倍,如果还不够,就是用它指定要扩容的大小minCapacity,然后判断minCapacity是否大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),如果大于,就取MAX_ARRAY_SIZE

(3)确定好新的长度后,即可调用Arrays.copyOf将原数组中的所有数据复制到扩容后的数组中

这篇关于Connor学Java - ArrayList的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/scottb/article/details/125569008
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/236069

相关文章

java实现延迟/超时/定时问题

《java实现延迟/超时/定时问题》:本文主要介绍java实现延迟/超时/定时问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java实现延迟/超时/定时java 每间隔5秒执行一次,一共执行5次然后结束scheduleAtFixedRate 和 schedu

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio

Spring Boot项目中结合MyBatis实现MySQL的自动主从切换功能

《SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能》:本文主要介绍SpringBoot项目中结合MyBatis实现MySQL的自动主从切换功能,本文分步骤给大家介绍的... 目录原理解析1. mysql主从复制(Master-Slave Replication)2. 读写分离3.

idea maven编译报错Java heap space的解决方法

《ideamaven编译报错Javaheapspace的解决方法》这篇文章主要为大家详细介绍了ideamaven编译报错Javaheapspace的相关解决方法,文中的示例代码讲解详细,感兴趣的... 目录1.增加 Maven 编译的堆内存2. 增加 IntelliJ IDEA 的堆内存3. 优化 Mave

Java String字符串的常用使用方法

《JavaString字符串的常用使用方法》String是JDK提供的一个类,是引用类型,并不是基本的数据类型,String用于字符串操作,在之前学习c语言的时候,对于一些字符串,会初始化字符数组表... 目录一、什么是String二、如何定义一个String1. 用双引号定义2. 通过构造函数定义三、St

springboot filter实现请求响应全链路拦截

《springbootfilter实现请求响应全链路拦截》这篇文章主要为大家详细介绍了SpringBoot如何结合Filter同时拦截请求和响应,从而实现​​日志采集自动化,感兴趣的小伙伴可以跟随小... 目录一、为什么你需要这个过滤器?​​​二、核心实现:一个Filter搞定双向数据流​​​​三、完整代码

SpringBoot利用@Validated注解优雅实现参数校验

《SpringBoot利用@Validated注解优雅实现参数校验》在开发Web应用时,用户输入的合法性校验是保障系统稳定性的基础,​SpringBoot的@Validated注解提供了一种更优雅的解... 目录​一、为什么需要参数校验二、Validated 的核心用法​1. 基础校验2. php分组校验3

Java Predicate接口定义详解

《JavaPredicate接口定义详解》Predicate是Java中的一个函数式接口,它代表一个判断逻辑,接收一个输入参数,返回一个布尔值,:本文主要介绍JavaPredicate接口的定义... 目录Java Predicate接口Java lamda表达式 Predicate<T>、BiFuncti

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Spring Security方法级安全控制@PreAuthorize注解的灵活运用小结

《SpringSecurity方法级安全控制@PreAuthorize注解的灵活运用小结》本文将带着大家讲解@PreAuthorize注解的核心原理、SpEL表达式机制,并通过的示例代码演示如... 目录1. 前言2. @PreAuthorize 注解简介3. @PreAuthorize 核心原理解析拦截与