ArrayList源码详解(常用方法源码分析)及 手动实现ArrayList
2019-03-03 17:56
591 查看
一、ArrayList详解
ArrayList是一个动态可变的数组。
特点:
- 元素可以重复
- 元素可以为null
- 数据插入有序
常用方法介绍:
- int size(); //集合中存储元素的个数
- boolean isEmpty(); // 判断集合是否为空 ,返回false:不为空。返回true:为空
- boolean contains(object o); // 判断当前集合是否包含object对象
- Iterator iterator() ; //迭代器,返回iterator实例
- Object[] toArray(); //将集合转换成数组
- T[] toArray(T[] a); //将集合转换成数组:
- boolean add; //添加元素
- boolean remove(Object o) //删除元素
- boolean containsAll(Collection<?> c) //判断入参集合是否属于当前集合
- boolean containsAll(Collection<? extends E> c) //对该集合添加新的子集合形成新的集合
- boolean containsAll(int index,Collection<? extends E> c)//在指定位置添加新的子集合形成新的集合
- void clear(); //将集合内所有的数字置为null
- boolean equals(Object o); //判断是否相等
- E get(int index); //通过指定位置来获取元素
- int indexOf(Object o); //判断当前元素在集合中的位置, 从前往后找
- int lastIndexOf(Object o); //判断当前元素在集合中的位置。从后往前找
- list subList(int fronIndex,int toIndex ) //找当前集合的子集,左闭右开,不包含右边的位置
JDK1.8源码分析:
- 继承关系
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable //ArrayList继承AbstractList,父类中对部分接口进行实现,实现了list接口提供的方法,Serializable说明该类能被序列化
- 底层数据结构
数组 - 基本属性和默认值
private static final long serialVersionUID = 8683452581122892189L;//与序列化有关的ID
private static final int DEFAULT_CAPACITY = 10; //默认数组容量大小为10
private static final Object[] EMPTY_ELEMENTDATA = {}; //默认数组为空
transient Object[] elementData; //存储元素的object类型的数组
private int size; //记录集合元素的个数
- 构造函数
1.带有一个参数(指定初始容量)的构造函数, public ArrayList(int initialCapacity) { //校验初始容量 if (initialCapacity > 0) { //new一个新的数组,容量为当前传入的大小 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
2.无参构造函数,构造一个具有初始容量为10的空列表 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
3.构造一个包含指定集合的元素的列表 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); //转换成数组 if ((size = elementData.length) != 0) { if (elementData.getClass() != Object[].class) //对element类型的数组进行强转 elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this.elementData = EMPTY_ELEMENTDATA; //重置为空数组 } }
- 增长方式
通过grow方法对ArrayList进行扩容: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);//调用hugeCapacity函数 elementData = Arrays.copyOf(elementData, newCapacity); //调用Arrays.copyOf将当前数组复制到扩容后的数组里 } private static int hugeCapacity(int minCapacity) { 20000 if (minCapacity < 0) //不合法的值 throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
- CRUD方法研究
//添加元素:add() 在添加元素之前,要判断数组是否已满,满了则要按1.5倍扩容,再添加元素 public boolean add(E e) { ensureCapacityInternal(size + 1); //调用ensureCapacityInternal方法 elementData[size++] = e; //添加完成后,数组元素的个数++ return true; } private void ensureCapacityInternal(int minCapacity) { //调用calculateCapacity和ensureExplicitCapacity方法 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity){ //判断当前数组是否为空 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //判断当前需要的数组和默认数组的大小,调用Math.max方法,返回最大的 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; //版本标记!用来记录对集合中元素所操作(增删改)的次数 if (minCapacity - elementData.length > 0) //容量不够用则扩容 grow(minCapacity); }
//删除元素:remove() 先找到元素存储位置(null和非null的对象在判断相等的时候不同) 注意:相同元素存在的情况下只需要找到从0号位置开始第一次出现的 public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { //null元素用==判断 fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { //非null则用equals判断相等 fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; //版本号 int numMoved = size - index - 1;//数组中要删除元素的后面的元素个数 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved);//调用System.arrayCopy将后面的数据往前复制一位 elementData[--size] = null; // 元素个数-1,clear to let GC do its work }
//获取元素:get() public E get(int index) { rangeCheck(index); return elementData(index); } private void rangeCheck(int index) { //对获取的下标进行范围检查, if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
//修改元素:set() public E set(int index, E element) { rangeCheck(index); //对下标进行范围检查 E oldValue = elementData(index); elementData[index] = element; //修改 return oldValue; }
手动实现ArrayList
class Arraylist{ //自定义一个ArrayList类,两个属性 private int[] array; //存放数据的数组 private int index; //存储元素的位置 初始为0; public Arraylist(){ this(5); //默认数组长度为5 this.index = 0; } public Arraylist(int size){ this.array = new int[size]; } public boolean isFull(){ //是否满 if (this.index >= array.length){ //满了,调用Arrays.copyOf进行2倍扩容 array = Arrays.copyOf(array,2*array.length); } return false; } public boolean add(int val){ //添加 if(!isFull()){ //没满可以直接添加,满了扩容后再添加 this.array[index++] = val; return true; } return false; } public int get(int i){ //获取 if(i>=0 && i< index) { int key = array[i]; return key; } throw new IndexOutOfBoundsException("越界"); } public boolean remove(int key){ //删除 for (int i = 0; i < array.length; i++) { if(array[i] == key) { array[i] = array[i + 1]; } } return false; } public void show(){ //打印 for(int i = 0;i < array.length;i++){ System.out.println(Arrays.toString(array)); return; } } } public class ArrayListDemo217 { public static void main(String[] args) { Arraylist arraylist = new Arraylist(); //添加 arraylist.add(1); arraylist.add(3); arraylist.add(5); arraylist.add(6); arraylist.add(8); arraylist.add(10); //打印 arraylist.show(); //获取 int val = arraylist.get(2); System.out.println(val); //删除 arraylist.remove(3); arraylist.show(); } }
此次总结学会了看源码,分析源码中ArrayList的构造和编程技巧,感觉源码里,每个方法都封装的特别好,让人一目了然并且代码的可读性超级强,刚入门的菜鸟都可以看懂。以后我写代码一定要像这样学习。
ArrayList和数组的异同之处:
- 初始化大小:数组必须指定大小,ArrayList不需要,可以动态扩容
- 存储数据类型:ArrayList只能存储引用类型。数组可以存储基本类型和引用类型。
- ArrayList比数组灵活。
相关文章推荐
- 56-58_数组_StringBuilder和StringBuffer_常用方法_方法链的实现_JDK源码分析_常见面试题
- java学习之旅56--数组_StringBuilder和StringBuffer的使用_常用方法_方法链的实现_JDK源码分析
- 【Java基础】Java学习之ArrayList源码常用方法分析
- 【Java】HashMap源码分析——常用方法详解
- 源码实现ArrayList的常用方法
- 源码分析-java-ArrayList-基本方法及实现
- Hibernate(三)Hibernate常用API详解及源码分析
- 集合框架源码分析三(实现类篇ArrayList,LinkedList,HashMap)
- 54-55_数组_String类的常用方法_JDK源码分析_内存分析
- 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]
- 常用集合ArrayList,LinkedList,HashMap,HashSet源码分析
- 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]
- JS实现导出Excel的五种方法详解【附源码下载】
- Java程序员从笨鸟到菜鸟之(五十二)细谈Hibernate(三)Hibernate常用API详解及源码分析
- 常用集合ArrayList,LinkedList,HashMap,HashSet源码分析
- jQuery源码分析10--方法链式调用的实现
- SSH学习(十)Hibernate常用API详解及源码分析
- 【MVC - 参数原理】详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]
- Cloudera Impala源码分析: SimpleScheduler调度策略详解包括作用、接口及实现等
- Android版数据结构与算法(二):基于数组的实现ArrayList源码彻底分析