扩容是元素还是数组_02 数组(附ArrayList源码分析)
定义
用一组连续的内存空间存储一组具有相同类型的数据的线性表数据结构。
优势
支持通过下标快速的随机访问数据,时间复杂度为O(1)。
劣势
通常情况下,插入和删除效率低下,每次操作后,需要进行后续元素的移位,时间复杂度为O(n)。
上面也说了通常情况下,那也就代表有时候我们也可以稍微优化一下,使其效率更高。
比如在插入的时候,直接将要插入位置k的元素挪到数组最后面,然后将新的元素放入位置k上,在部分情况下,就能满足我们的需求。
再说删除操作,我们可以在删除的时候,不急着将后面元素前移,而是先标记一下,等空间不够时,统一进行位移,听起来有点类似JVM的标记清除垃圾回收算法。
介绍完数组,再说一下数组类型的容器类,以Java中的ArrayList为例。
ArrayList主要为我们做了什么事情呢?
1. 封装操作细节:插入、删除时搬移其他元素的操作;
2. 动态扩容;
3. 运行时,当碰到并发问题时,通过抛出CME(ConcurrentModificationException)提醒使用者。
浅析ArrayList源码
插入
1. 在最后插入
public boolean add(E e) {ensureCapacityInternal(size + 1); // 扩容,并增加modCount值,用于做并发检测的依据elementData[size++] = e; // 插入到最后return true;
}
2. 在指定位置插入
public void add(int index, E element) {rangeCheckForAdd(index); // 判断是否越界ensureCapacityInternal(size + 1); // 扩容,并增加modCount值,用于做并发检测的依据System.arraycopy(elementData, index, elementData, index + 1, size - index); // 数据移位,使用JVM提供的数据拷贝实现elementData[index] = element; // 插入到指定位置size++;
}
删除
- 删除指定元素
public boolean remove(Object o) {if (o == null) { // null的执行分支不同,因为null是没有equals方法的for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index); // 删除return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index); // 删除return true;}}return false;
}
private void fastRemove(int index) {modCount++; // 增加modCount值,用于做并发检测的依据int numMoved = size - index - 1; // 计算需要前移的元素数量if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index, numMoved); // 数据移位,使用JVM提供的数据拷贝实现elementData[--size] = null; // 从数组中清除元素,方便GC回收
}
2. 删除指定位置元素
public E remove(int index) {rangeCheck(index); // 判断是否越界modCount++; // 增加modCount值,用于做并发检测的依据E oldValue = elementData(index); // 获取数组中对应位置元素,如果index<0是在这里抛出越界异常的int numMoved = size - index - 1; // 计算需要前移的元素数量if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index, numMoved); // 数据移位,使用JVM提供的数据拷贝实现elementData[--size] = null; // 从数组中清除元素,方便GC回收return oldValue;
}
扩容
尝试将数组大小扩容到原来的1.5倍
private void grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容到原来的1.5倍if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity); // 将原来的数据拷贝到新数组中,赋值
}
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // 溢出了throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
并发检测相关
通过添加删除时修改的modCount进行检测,如果在遍历容器元素的过程中,modCount发生了修改,就会抛出CME。
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();
}
如果有迭代删除元素的需求,通常使用迭代器Iterator,其中的remove操作,因为代码
expectedModCount = modCount; // iterator中的remove记录了修改
的存在,支持迭代过程中删除元素(如果是迭代过程中,其他线程对该容器进行了add或者remove操作,仍然会抛出CME)。
扩容是元素还是数组_02 数组(附ArrayList源码分析)相关推荐
- arraylist扩容是创建新数组吗 java_Java集合干货——ArrayList源码分析
前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体实现,一些基本的都也知道,譬 ...
- C++实现array left rotation数组左旋转(附完整源码)
C++实现array left rotation数组左旋转算法 C++实现array left rotation数组左旋转算法完整源码(实现,main函数测试) C++实现array left rot ...
- 数组、链表、LinkedList源码分析、跳表
一.数组 1.什么是数组 数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据 线性表:数据排成像一条线一样的结构.每个线性表上的数据最多只有前和后两个方向.其实除了数组,链 ...
- 基于边缘的图像分割——分水岭算法(watershed)算法分析(附opencv源码分析)
最近需要做一个图像分割的程序,查了opencv的源代码,发现opencv里实现的图像分割一共有两个方法,watershed和mean-shift算法.这两个算法的具体实现都在segmentation. ...
- C语言实现动态数组dynamic array(附完整源码)
C语言实现动态数组dynamic array 数组结构体的定义 实现以下7个接口 完整头文件 完整源文件 完整 main 测试文件 数组结构体的定义 typedef struct dynamic_ar ...
- JavaScript:实现将 base64 字符串转换为字节数组算法(附完整源码)
JavaScript:实现将 base64 字符串转换为字节数组算法 function base64ToBuffer (b64) {// The base64 encoding uses the fo ...
- python:实现9×9二维数组数独算法(附完整源码)
python:实现9×9二维数组数独算法 from __future__ import annotationsMatrix = list[list[int]]# assigning initial v ...
- python:实现数组去重算法(附完整源码)
python:实现数组去重算法 ls = [8,2,5,4,4,5,5] ls.sort() ls1 = [ls[
- JavaScript实现以数组形式返回斐波那契数列fibonacci算法(附完整源码)
JavaScript实现以数组形式返回斐波那契数列fibonacci算法(附完整源码) fibonacci.js完整源代码 fibonacci.js完整源代码 export default funct ...
最新文章
- MYSQL:1045Access denied for user 'root'@'localhost
- 汇编程序开发环境搭配
- Zabbix实战之客户端自动发现
- opengl加载显示3D模型off类型文件
- 为了你,我一定要写诗
- 通俗理解数字签名,ssl数字证书和https
- JAVA语法——自动类型转换和强制类型转换
- canvas 2.0
- 根据TTL值判断目标主机的类型
- AK的故事之英语学习篇(mistake)
- chrome支持的java版本下载_安装Chrome Java插件
- Tcpping 替代ping测试网路延迟
- APT案例调研和研究
- python爬虫爬取百度图片
- 研究生师兄谈SCI论文写作心得
- Hadoop报错Permissions incorrectly set for dir /tmp/hadoop-LeiHanhan/nm-local-dir/filecache, should be
- doodoo.js配置教程
- 江南春:在不确定的市场,找到确定性的增长
- 语音视频自动生成字幕在线工具介绍
- 古代赌局 俗话说:十赌九输。因为大多数赌局的背后都藏有阴谋。不过也不尽然,有些赌局背后藏有的是:“阳谋”。 有一种赌局是这样的:桌子上放六个匣子,编号是1至6。多位参与者(以下称玩家)可以把