栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java核心技术-集合-Collection接口-List-ArrayList

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java核心技术-集合-Collection接口-List-ArrayList

ArrayList 一、概述

ArrayList是平时开发中最常用的集合,底层是数组,相当于动态数组。与java中的数组相比,它的长度能动态增长

1、ArrayList的特点

它是基于数组的List实现类它的元素可以有null它进出是有序的不同步,线程不安全,效率高查询快,增删慢相较与底层是链表的linkedList集合,不需要额外的空间去维护底层链表结构占用空间更小可以动态的调整容量 2、所有方法

方法名参数作用
add任何对象添加元素到集合中
size获取当前集合中元素的数量
getint 角标获取传入角标下的元素
addAllColletion集合将另一个集合合并到当前集合中
addAllindex插入位置的角标,Colletion,集合将另一个集合从指定位置开始插入
contains元素对象判断传入的元素是否在当前集合中存在
indexOf元素对象根据传入的对象返回对象角标
remove元素对象根据传入的对象判断是否存在,存在就删除返回true否则返回false
remove角标根据传入的角标判断是否存在,存在就删除并将删除的元素返回
removeAllCoolletion集合根据传入的集合批量删除
removeIfSteam流对象按条件删除
clear清空集合
3、继承体系

ArrayList继承了AbstractList,实现了Cloneble,RandomAccess,Serializable

实现List,提供了基础的添加,删除,遍历等操作实现了RandomAccess,提供了随机访问的操作实现了Cloneble,可以被克隆实现了Serializable,可以被序列化 二、源码分析

1、成员变量
	//序列号
 private static final long serialVersionUID = 8683452581122892189L;

    
    private static final int DEFAULT_CAPACITY = 10;

    
    private static final Object[] EMPTY_ELEMENTDATA = {};

    
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    
    transient Object[] elementData; 

    
    private int size;
2、构造方法
	//根据指定的传入的大小创建集合(底层数组)
 public ArrayList( int initialCapacity){
 	  //如果大于0就创建initialCapacity大小的数组
      if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
        //否则默认用空数组
      } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
      } else {
        throw new IllegalArgumentException("Illegal Capacity: " +
            initialCapacity);
      }
    }

	//默认空参构造器,初始化为空数组,只有当传入一条或一条以上的的元素才会扩容为10,否则默认为空
    public ArrayList() {
      this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

	//构造一个包含指定元素的列表
    public ArrayList(Collection < ? extends E > c){
      //将传入的集合转换为数组
      Object[] a = c.toArray();
      //如果传入的集合长度不等于0
      if ((size = a.length) != 0) {
      	//如果是ArrayList,直接赋值
        if (c.getClass() == ArrayList.class) {
          elementData = a;
        } else {
        	//转换成数组后再赋值
          elementData = Arrays.copyOf(a, size, Object[].class);
        }
        //如果长度等于0,赋值空对象
      } else {
        elementData = EMPTY_ELEMENTDATA;
      }
    }

当以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加操作时,才能真正的分配容量。即向数组添加第一个元素时,数组扩容为10

3、内部类
(1)private class Itr implements Iterator  
(2)private class ListItr extends Itr implements ListIterator  
(3)private class SubList extends AbstractList implements RandomAccess  
(4)static final class ArrayListSpliterator implements Spliterator  

ArrayList有四个实现类:
Itr是实现了Iterator接口,同时重写了里面的hasNext(), next(), remove() 等方法;
ListItr是实现了ListIterator接口,同时重写了hasPrevious(), nextIndex(), previousIndex(), previous(), set(E e), add(E e);

4、核心方法 4.1、4个添加元素相关的方法

== 增和删是ArrayList最重要的部分==

  //添加一个元素到list的末尾
     public boolean add(E e) {
     	//判断是否需要扩容
        ensureCapacityInternal(size + 1);
        //已有元素+1,并把元素添加在正确角标下(注意并不是添加在元素+1角标下,因为++在后)
        elementData[size++] = e;
        return true;
    }
  	
    private void add(E e, Object[] elementData, int s) {
    	//首先判断当前元素数量是否等于当前数组的长度,如果是进行扩容
        if (s == elementData.length)
            elementData = grow();
        //不等于直接添加元素到数组中    
        elementData[s] = e;
        //加一操作,记录当前元素数量
        size = s + 1;
    }
    
    //在指定位置添加一个元素
    public void add(int index, E element) {
    	//判断角标是否合法
        rangeCheckForAdd(index);
		//判断是否需要扩容
        ensureCapacityInternal(size + 1); 
        //将传入的角标之后所有的数据向后移动一格
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        //在指定角标中添加元素
        elementData[index] = element;
        //元素长度+1
        size++;
    }
     public boolean addAll(Collection c) {
        //将传入的集合转为数组并赋值到a
        Object[] a = c.toArray();
        //获取当前传入集合的长度
        int numNew = a.length;
        //判断是否需要扩容
        ensureCapacityInternal(size + numNew);
        //将传入的集合添加到原集合的后面
        System.arraycopy(a, 0, elementData, size, numNew);
        //原集合长度+传入过来的集合长度
        size += numNew;
        return numNew != 0;
    }
   public boolean addAll(int index, Collection c) {
   		//判断角标是否合理(如果传入角标大于当前当前数据量并小于0不合理)
        rangeCheckForAdd(index);
		//将传入的集合转为数组并赋值到a
        Object[] a = c.toArray();
          //获取当前传入集合的长度
        int numNew = a.length;
           //判断是否需要扩容
        ensureCapacityInternal(size + numNew);  
        
        int numMoved = size - index;
        //判断是否需要后移原数组(逻辑如果传入的角标是刚好是当前集合的长度不需要后移)
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,numMoved);
        //把新的集合添加到指定位置
        System.arraycopy(a, 0, elementData, index, numNew);
         //原集合长度+传入过来的集合长度
        size += numNew;
        return numNew != 0;
    }
4.2、扩容相关方法

对集合进行添加元素时都会判断是否进行扩容,调用grow方法

	//确保内部容量够用
   private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //判断是否需要扩容
   private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 判断是否需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //计算容量,判断原数组是否是空数组
   private static int calculateCapacity(Object[] elementData, int minCapacity) {
   		//如果是空返回默认扩容长度或传入数
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    //扩容核心方法
     private void grow(int minCapacity) {
     	//获取原数组长度
        int oldCapacity = elementData.length;
        //扩充1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果说传入的数大于扩充1.5倍的值
        if (newCapacity - minCapacity < 0)
        	//赋值成传入数
            newCapacity = minCapacity;
        //如果需要扩容的数大于集合最大数 判断是给intger最大数还是集合最大数    
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            //扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
4.3、大数据量插入

当大数据量插入时可以尝试两种方式:
1、使用ArrayList(int initialCapacity)这个有参构造器,在创建时就声明一个较大的大小,这样解决了频繁拷贝问题,但是需要我们提前预知数据量的大小,而且也会一直占用内存
2、使用ensureCapacity(int minCapacity)方法,其内部也是调用ensureExplicitCapacity(int minCapacity)进行扩容。

4.4、remove()方法
    public E remove(int index) {
    //检查index的合理性
        rangeCheck(index);
        modCount++;
        //通过传入的角标找到对应的元素
        E oldValue = elementData(index);
        //开始计算是否需要移动和移动的位置,
        int numMoved = size - index - 1;
        if (numMoved > 0)
        //将需要删除的元素后面那些元素整体向前移一位(这时候最后一位元素还在老位置)
      System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将--size上的位置赋值为null,让gc(垃圾回收机制)更快的回收它。
        elementData[--size] = null; 
        return oldValue;
    }
4.5、set()方法 4.6、get()方法 4.7、indexOf()方法 4.8、contains()方法 4.9、toArray()方法 5、分析System.arraycopy()和 Arrays.copyOf()方法 5.1、分析System.arraycopy()方法

System.arraycopy():将指定数组中的数组从指定位置开始复制到目标数据的指定位置

// src:源对象
// srcPos:源对象对象的起始位置
// dest:目标对象
// destPost:目标对象的起始位置
// length:从起始位置往后复制的长度。
// 这段的大概意思就是解释这个方法的用法,复制src到dest,复制的位置是从src的srcPost开始,到srcPost+length-1的位置结束,复制到destPost上,从destPost开始到destPost+length-1的位置上
public static void arraycopy(Object src, int srcPos, Object dest, int destPos,
             int length)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/709044.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号