`

源码阅读之ArrayList

阅读更多
常用方法
1, 其实有两个ArrayList。一个是java.util包下面的。一个java.utils.Collections这个工具类内部类。后者其实只是Collections一系列方法的返回对象.


2,ArrayList继承的接口有List,RandomAccess和Conable和serializable 。换句话说。其没有其他的集合语义。
public class ArrayList<E> extends AbstractList<E>   
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable 

3,成员变量只有elementData和size两个成员变量。觉得还是简单的。这也就意味着,elementData是全部存储对象的。没有任何的保存不再的东西的可能性.,也可以看出,ArrayList采用数组的方式存放对象。
 /**
     * ArrayList底层维护的Object数组
     */
    private transient Object[] elementData;

    /**
     * 数组的长度
     *
     * @serial
     */
    private int size;


4,构造函数分两类。
一类是无参和(int), 其实就是一个初始化elementData的长度。无参的默认长度为10
另一位是(Collection): 用了Collection.toArray进行转换。
//无参构造方法,会直接去调用有参构造方法
 public ArrayList() {
	this(10);
    }

//有参构造方法
 public ArrayList(int initialCapacity) {
	super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
	this.elementData = new Object[initialCapacity];
    }

//集合构造方法
  public ArrayList(Collection<? extends E> c) {
	elementData = c.toArray();
	size = elementData.length;
	// c.toArray might (incorrectly) not return Object[] (see 6260652)
	if (elementData.getClass() != Object[].class)
	    elementData = Arrays.copyOf(elementData, size, Object[].class);
    }


5,trimToSize:内存紧张的时候会用到。
 public void trimToSize() {
	modCount++;
	int oldCapacity = elementData.length;
	if (size < oldCapacity) {
            elementData = Arrays.copyOf(elementData, size);
	}
    }

6.ensureCapacity:扩充数组长度。扩展长度是原长度old×1.5+1。
 public void ensureCapacity(int minCapacity) {
	modCount++;
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;
    	    if (newCapacity < minCapacity)
		newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
	}
    }


7,indexOf和lastIndexOf。重写了一下,直接访问数组。觉得Iterator其实性能上不好。只是语义上与Collection相对.indexOf和lastIndexOf是ArrayList中用于获取对象所在位置的方法,indexOf为从前往后找,lastIndexOf为从后往前找。
 public int indexOf(Object o) {
	if (o == null) {
	    for (int i = 0; i < size; i++)
		if (elementData[i]==null)
		    return i;
	} else {
	    for (int i = 0; i < size; i++)
		if (o.equals(elementData[i]))
		    return i;
	}
	return -1;
    }

 public int lastIndexOf(Object o) {
	if (o == null) {
	    for (int i = size-1; i >= 0; i--)
		if (elementData[i]==null)
		    return i;
	} else {
	    for (int i = size-1; i >= 0; i--)
		if (o.equals(elementData[i]))
		    return i;
	}
	return -1;
    }

8, 重写了clone方法,其实就是数组copy加上modCount重置。
  public Object clone() {
	try {
	    ArrayList<E> v = (ArrayList<E>) super.clone();
	    v.elementData = Arrays.copyOf(elementData, size);
	    v.modCount = 0;
	    return v;
	} catch (CloneNotSupportedException e) {
	    // this shouldn't happen, since we are Cloneable
	    throw new InternalError();
	}
    }


9,toArray(T[] a)觉得实现有点怪。具体规则为。
如果a的size小于List的size,那么就返回一个新的数组。不影响a。
反之,a的size大于List。那么就是把list全部Copy到a,然后在List的size的位置,把其设为null。后面的数据不变,然后返回a。换句话说影响了a。
所以觉得,还是怎么说呢,创建一个新的a,空的a丢进去。比较保险。因为都是用了复制。就是多了一个创建的性能消耗。toArray();直接调用Arrays的拷贝方法。
public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
	System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }

 public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }


10,set方法的都有一个rangcheck方法。操作的是就是把删除数组索引位置的值,然后把后面的数据前移动。
 public E set(int index, E element) {
	RangeCheck(index);

	E oldValue = (E) elementData[index];
	elementData[index] = element;
	return oldValue;
    }


11.add(element);方法,首先检查扩充容量,然后把e的值付给当前数据组的size位置上,size+1返回。
 public boolean add(E e) {
	ensureCapacity(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
    }

12.add(Index,element)方法,就是会使用System中copy数组的方法。
addAll(collection)只是copy了一次数组,addALL(index,collection)则copy了两次。
 public void add(int index, E element) {
	if (index > size || index < 0)
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);

	ensureCapacity(size+1);  // Increments modCount!!
	System.arraycopy(elementData, index, elementData, index + 1,
			 size - index);
	elementData[index] = element;
	size++;
    }

 public boolean addAll(Collection<? extends E> c) {
	Object[] a = c.toArray();
        int numNew = a.length;
	ensureCapacity(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
	return numNew != 0;
    }

  public boolean addAll(int index, Collection<? extends E> c) {
	if (index > size || index < 0)
	    throw new IndexOutOfBoundsException(
		"Index: " + index + ", Size: " + size);

	Object[] a = c.toArray();
	int numNew = a.length;
	ensureCapacity(size + numNew);  // Increments modCount

	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;
    }



13,remove方法,是用equal进行判断的。remove第一出线的。调用了一个fastRemove的内部方法,其省去了边界检查。其实也还是很System copy。remove(int)的实现比remove(e)多了一个数组范围检测,但少了数组元素的查找,因此性能会更好
 public E remove(int index) {
	RangeCheck(index);

	modCount++;
	E oldValue = (E) elementData[index];

	int numMoved = size - index - 1;
	if (numMoved > 0)
	    System.arraycopy(elementData, index+1, elementData, index,
			     numMoved);
	elementData[--size] = null; // Let gc do its work

	return oldValue;
    }
 public boolean remove(Object o) {
	if (o == null) {
            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++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
    }

14.get(index)方法,在arrayList维护的数组中取出索引为index的值,前提是要保证index的值没有超过数组的最大长度。
 public E get(int index) {
	RangeCheck(index);

	return (E) elementData[index];
    }

15.contains(o)判断数组中是否存在某元素。其做法为遍历整个ArrayList中已有元素,如o为null,则直接判断已有元素是否为null,如为null返回true,如果o不为null,则直接通过判断o.equals和元素是否相等,如相等返回true.
 public boolean contains(Object o) {
	return indexOf(o) >= 0;
    }

15.size();直接返回ArrayList中维护的size成员变量。
 public int size() {
	return size;
    }

16.isEmpty();判断ArrayList中维护的size的值是否为0
public boolean isEmpty() {
	return size == 0;
    }

注意要点:
1.ArrayList基于数组方式实现,无限制容量。添加元素时,就是想ArrayList维护的数组中添加元素。
2.ArrayList在执行插入时可能扩充,在删除元素时候,并不会减小数组的容量(如希望减小数组的容量可以调用ArrayLust的trimToSize()方法,在查找元素要遍历数组时,对于非null元素采用equals的方式寻找)
3.ArrayList是非线程安全的
4.ArrayList删除元素操作时,需要将数组元素向前移动,代价比较高。
5.集合当中放置的是对象的引用,无法放置原生数据类型。我们需要使用原生数据类型的包装类。取出的时候,要进行强制类型转换将其转换成真正的类型(放置进去的类型).
分享到:
评论

相关推荐

    使用对象ArrayList填充DataGrid,C#源代码ArrayList MyList = new ArrayList();

    本篇将详细讲解如何使用对象ArrayList来填充C#中的DataGrid,并通过示例代码进行说明。 首先,ArrayList是.NET Framework中的一个类,它继承自System.Collections.ArrayList,主要用于存储动态大小的可变数组。...

    ArrayList源码分析

    本篇文章将深入探讨ArrayList的源码,了解其内部实现机制,以及在实际编程中如何有效地使用ArrayList。 1. **ArrayList的构造函数** ArrayList提供了多个构造函数,包括无参构造、指定容量构造和初始化容量并赋值...

    jdk源码阅读一:ArrayList

    ### jdk源码阅读一:ArrayList #### 一、ArrayList简介与特性 ArrayList作为Java集合框架中的重要组成部分,被广泛应用于各种应用场景。它的主要特点包括: - **基于数组实现**:ArrayList采用数组作为其底层数据...

    ArrayList源码阅读笔记

    ArrayList源码阅读笔记 -- 介绍了ArrayList 普通增删改查的过程,从构造空参构造方法,然后添加元素,修改元素,删除元素,获取元素.

    ArrayList源码Jdk1.8

    ### ArrayList源码解析(JDK 1.8) #### 概述 `ArrayList`是Java集合框架中的一个核心组件,它提供了动态数组的功能。与固定大小的数组不同,`ArrayList`可以随着元素的增加而自动扩展其容量。在JDK 1.8中,`...

    ArrayList源码分析(含jdk1.8).pdf

    在了解ArrayList的源码分析时,我们主要探讨其在Java Development Kit (JDK) 1.8中的实现。ArrayList是一个非常重要的集合框架,用于动态数组的实现,其功能类似于数组,但可以在运行时动态调整大小。它是一个非线程...

    c#重写ArrayList源代码

    尽管如此,了解ArrayList的内部工作原理以及如何重写其源代码,对于深化对C#和面向对象编程(OOP)的理解仍然非常有价值。 ArrayList的源代码重写通常涉及以下几个关键部分: 1. **构造函数**:ArrayList有一个无...

    ArrayList源码.zip

    本压缩包文件“ArrayList源码.zip”包含ArrayList的源代码,可以帮助我们深入理解其内部工作原理和优化策略。 ArrayList的核心实现是通过一个Object类型的数组来存储元素。当添加元素时,如果当前容量不足,...

    ArrayList源码

    反编译系统的,只是做个例子。 为了凑够20个字,我多打几个字……

    毕业设计源码之ArrayList,LinkList链表接口实现.zip

    3. **源码阅读**:通过阅读ArrayList和LinkedList的源码,可以理解Java集合框架内部的实现机制,比如扩容机制、元素查找、插入和删除的具体步骤。 4. **项目部署**:项目部署说明.zip文件可能包含部署该项目到...

    C#160使用对象ArrayList填充DataGrid 源代码

    为了将ArrayList中的数据填充到DataGrid,我们需要创建一个DataTable并映射到ArrayList中的对象,因为DataGrid默认使用DataTable作为数据源: ```csharp DataTable dataTable = new DataTable(); dataTable.Columns...

    关于 ArrayList get(0)的异常JDK源码跟进

    通过阅读和分析JDK源码,我们可以深入理解ArrayList的工作原理,这对于排查和解决实际问题非常有帮助。在遇到类似问题时,可以先检查索引是否合法,再检查ArrayList是否已添加元素,最后结合源码分析异常产生的具体...

    arrayList源代码

    ### ArrayList源代码解析 在Java集合框架中,`ArrayList`是一个非常重要的类,它实现了`List`接口,并提供了基于动态数组的数据结构。本篇将详细分析`ArrayList`的源码,帮助读者理解其内部实现机制。 #### 类定义...

    ArrayList扩容机制源码解析.md

    本资源根据个人学习和总结,主要介绍Java中ArrayList扩容机制源码的解析,主要包含文字和代码等内容,以源码的形式进行分析,详细介绍了ArrayList扩容机制的整个流程,特此将该资源分享

    ArrayList动态删除 自定义Adapter (附源码)

    ArrayList动态删除与自定义Adapter是Android开发中的常见操作,它涉及到数据存储、用户界面更新以及适配器模式的运用。...通过阅读和分析提供的源码,可以进一步加深对这一机制的理解,并应用于实际项目中。

    ArrayList转化为DataTable

    在实际开发中,这种方法特别适用于需要将从数据库或其他源获取的非结构化数据(如ArrayList)转换为可以进一步处理或绑定到控件(如DataGridView)的结构化数据(如DataTable)。此外,.NET Framework还提供了其他...

    java毕业设计之ArrayList,LinkList链表接口实现源码.zip

    "yuanma"可能是项目源代码的目录或者压缩文件,其中包含了ArrayList和LinkedList的具体实现以及其他相关代码。 通过这个项目,学生不仅可以学习到ArrayList和LinkedList的数据结构和算法,还能实践Java编程、数据库...

    JDK8的ArrayList源码文件

    JDK8的ArrayList源码文件

Global site tag (gtag.js) - Google Analytics