`
Moyunyu
  • 浏览: 14662 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

(一). 基于数组的列表实现ArrayList

阅读更多


这些天一直纠结于散列表的总结, 感觉自己对散列表的理解还可以, 源代码也深究了一些, 但是一到要写的时候就找不到好的思路, 只好从基本的开始写, 希望能为后续的散列表总结找到一些思路...

ArrayList是基于数组实现的, 它在一个连续的储存块中储存元素, 不过与数组不同的是: ArrayList可以进行动态的增长, 通俗一点说就是: 当数组不足以容纳更多的元素时, 我们就再建立一个新的,更长的数组, 将原数组的元素拷贝到新数组里, 再对这个新数组进行操作. 

 

首先分析一下它的基本功能:


1.添加操作:将指定索引位置后所有元素向后移动, 再将要插入的数据插入到空出来的指定索引位置.

 

2.删除操作:

删除操作和插入操作有点类似, 是将指定索引后的元素都向前移动, 想当于插入操作的逆过程.


3.查找操作:

1)给定索引, 可直接查找指定索引出的元素

2)给定值, 则需要遍历


4.修改操作:  

给定索引直接替换原值


下面看一下具体的代码实现:


 

package List;

/**
 * 基于数组的列表集合
 * 
 * @author MOYUNYU
 * 
 */
public class ArrayListImpl {

	// 保存元素的数组
	private Object[] data;
	// 列表中所添加的元素个数
	private int size = 0;

	/**
	 * 无参的构造方法, 数组初始大小默认为10
	 */
	public ArrayListImpl() {
		this(10);
	}

	/**
	 * 指定列表的初始大小
	 * 
	 * @param Capacity
	 */
	public ArrayListImpl(int capacity) {
		// 判断给的容量是否合法
		if (capacity < 0) {
			throw new IllegalArgumentException("列表的容量不能是负数");
		}
		// 实例化数组
		this.data = new Object[capacity];
	}

	/**
	 * 在列表尾部插入对象
	 * 
	 * @param o
	 *            要插入的对象
	 * @return 插入成功则返回true
	 */
	public boolean add(Object o) {
		// 检查是否需要扩容
		checkCapacity(-1, null);
		// 添加对象到列表尾端
		data[size] = o;
		// 列表长度加一
		size++;
		// 添加成功, 返回true
		return true;
	}

	/**
	 * 在指定索引位置插入对象
	 * 
	 * @param index
	 *            指定的索引位置
	 * @param o
	 *            要插入的对象
	 */
	public void add(int index, Object o) {
		// 尾部直接插入
		if (index == size + 1) {
			add(o);
		}
		// 检查索引值的合法性
		else if (rangeCheck(index)) {
			// 如果不需要扩容, 则直接进行操作, 否则进行扩容插入
			if (size < data.length) {
				// 将索引index-1后的所有对象后移
				System.arraycopy(data, index, data, index + 1, size - index);
				// 直接插入对象
				data[index] = o;
			} else {
				// 扩容, 并插入对象
				checkCapacity(index, o);
			}
			// 列表长度加一
			size++;
		}
	}

	/**
	 * 删除指定位置的对象并将删除的对象返回
	 * 
	 * @param index
	 *            指定的索引位置
	 * @return 索引越界返回null, 否则返回删除的对象
	 */
	public Object remove(int index) {
		if (index == size) {
			throw new IndexOutOfBoundsException("你指定的索引越界,列表大小 : " + size
					+ ", 你指定的索引: " + index);
		}
		// 索引检查
		if (rangeCheck(index)) {
			// 保存对象
			Object o = data[index];
			if (index == size) {
				data[index] = null;
			} else {
				// 将后边的前移
				System.arraycopy(data, index + 1, data, index, size - index - 1);
			}
			// 列表长度加一
			size--;
			// 将要删除的对象返回
			return o;
		}
		return null;
	}

	/**
	 * 删除列表尾部的对象
	 * 
	 * @param o
	 *            尾部的对象
	 * @return 删除成功则返回true, 没有指定对象返回false
	 */
	public boolean remove(Object o) {
		// 遍历数组, 寻找指定对象
		for (int i = 0; i < size; i++) {
			if (o.equals(data[i])) {
				remove(i);
				return true;
			}
		}
		// 没有找到返回false
		return false;

	}

	/**
	 * 查找指定索引位置上的对象
	 * 
	 * @param index
	 *            指定的索引位置
	 * @return 该位置上的对象
	 */
	public Object find(int index) {
		// 检查索引的合法性
		rangeCheck(index);
		// 返回指定索引位置的对象
		return data[index];
	}

	/**
	 * 判断列表中是否含有指定对象
	 * 
	 * @param o
	 *            指定的对象
	 * @return 含有指定对象返回true
	 */
	public boolean contain(Object o) {
		// 循环遍历列表
		for (int i = 0; i < size; i++) {
			if (data[i].equals(o)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 修改指定位置上的对象
	 * 
	 * @param index
	 *            指定的索引位置
	 * @param o
	 * @return 修改成功返回true
	 */
	public Object change(int index, Object o) {
		// 检查指定索引位置的合法性
		rangeCheck(index);
		// 储存原对象
		Object old = data[index];
		// 替换
		data[index] = o;
		return old;

	}

	/**
	 * 检查给定的索引是否合法
	 * 
	 * @param index
	 *            给定元素
	 * @return 越界抛出异常, 正常true
	 */
	public boolean rangeCheck(int index) {
		if (index > size || index < 0) {
			throw new IndexOutOfBoundsException("你指定的索引越界,列表大小 : " + size
					+ ", 你指定的索引: " + index);
		}
		return true;
	}

	/**
	 * 作用1: 扩容, 复制原数组 作用2: 扩容, 复制原数组并在指定索引插入对象, 仅当index==-1, o==null
	 */
	public void checkCapacity(int index, Object o) {
		if (size >= data.length) {
			// 实例化一个新的数组
			Object[] newData = new Object[size * 3];
			// 如果index是-1, 直接拷贝即可, 否则, 需要将指定索引位置空下
			if (index == -1 && o == null) {
				// 将原数组中的对象拷贝过来
				System.arraycopy(data, 0, newData, 0, size);
			} else {
				// 将要插入索引位置前面的对象拷贝
				System.arraycopy(data, 0, newData, 0, index);
				// 插入对象
				newData[index] = o;
				// 将要插入索引位置后面的对象拷贝
				System.arraycopy(data, index, newData, index + 1, size - index);
			}
			// 将引用交给data
			data = newData;
			// 让GC处理
			newData = null;
		}
	}

	/**
	 * 获取列表的大小
	 * 
	 * @return 列表的大小
	 */
	public int getSize() {
		return this.size;
	}

	/**
	 * 打印列表中的数据
	 */
	public void printArrayList() {
		for (int i = 0; i < size; i++) {
			if (i == 0) {
				System.out.print("{ " + data[i]);
			} else if (i > 0 && i < size - 1) {
				System.out.print(", " + data[i]);
			} else if (i == size - 1) {
				System.out.print(", " + data[i] + " }");
			}
		}
	}

}


下面请看测试:

 

package List;

public class Demo {
	public static void main(String[] args) {
		ArrayListImpl a = new ArrayListImpl();
		long start = System.currentTimeMillis();
		for (int i = 0; i < 2000001; i++) {
			a.add(i, i);
		}

		long end = System.currentTimeMillis();
		System.out.println("自实现列表的需要所需要的时间为 : " + (end - start));

	}
}

 

 

 

结果为:

自实现列表的需要所需要的时间为 : 486

系统列表所需要的时间为 : 453

 

 

package List;

import java.util.ArrayList;

public class STest {
	public static void main(String[] args) {
		ArrayList a = new ArrayList();
		long start = System.currentTimeMillis();
		for (int i = 0; i < 2000001; i++) {
			a.add(i, i);
		}
		 for (int i = 0; i < 2000001; i++) {
		 a.set(i, i);
		 }
		long end = System.currentTimeMillis();
		System.out.println("系统列表所需要的时间为 : " + (end - start));
	}
}

 


结果为:

自实现列表的需要所需要的时间为 : 967

系统列表所需要的时间为 : 811


 

package List;

public class Demo {
	public static void main(String[] args) {
		ArrayListImpl a = new ArrayListImpl();
		long start = System.currentTimeMillis();
		for (int i = 0; i < 2000001; i++) {
			a.add(i, i);
		}
		for (int i = 0; i < 2000001; i++) {
			a.change(i, i);
		}
		for (int i = 2000000; i >= 0; i--) {
			a.remove(i);
		}
		long end = System.currentTimeMillis();
		System.out.println("自实现列表的需要所需要的时间为 : " + (end - start));

	}
}

 

 

  结果为:

自实现列表的需要所需要的时间为 : 998

系统列表所需要的时间为 : 842


总结: 

1. ArrayList可以高效的随机访问元素

2. 在ArrayList中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;

3. ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间


也就是说:  当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能; 但是如果需要频繁的进行插入删除等操作, ArrayList的缺点就会很明显了, 这时可以选择LinkedList, 有关LinkedList的实现, 将在下一篇博客中分析.





2
1
分享到:
评论

相关推荐

    java中数组列表ArrayList的使用.doc

    `ArrayList`的底层实现是基于数组结构,因此它在查询元素时具有较快的速度(时间复杂度接近O(1)),但在插入或删除元素时性能较低(时间复杂度为O(n))。 #### 二、ArrayList的特点 - **动态调整大小**:`ArrayList...

    java中数组列表ArrayList的使用[归类].pdf

    在Java编程语言中,ArrayList是`java....总之,ArrayList是Java中常用的动态数组实现,适用于需要灵活增删元素且对随机访问性能有较高要求的场景。了解并熟练掌握ArrayList的使用和特性对于Java开发者来说至关重要。

    c# 数组与集合(ArrayList)游戏开发高级使用举例

    ArrayList内部基于数组实现,但提供了更多的灵活性。当你不确定需要存储多少元素或者需要频繁添加、删除元素时,ArrayList是一个更好的选择。例如,在游戏中,如果玩家可以随时加入或退出队伍,使用ArrayList来存储...

    java提高篇(二一)-----ArrayList.pdf

    ArrayList 底层采用数组实现,所以它的操作基本上都是基于对数组的操作。ArrayList 提供了三个构造函数: 1. ArrayList():默认构造函数,提供初始容量为 10 的空列表。 2. ArrayList(int initialCapacity):构造一...

    数组模仿ArrayList

    它内部基于动态数组实现,提供了灵活的增删改查功能,同时保持了元素的有序性。本篇将深入探讨如何使用数组来模仿ArrayList的功能,以便理解其底层原理。 首先,数组是最基本的数据结构,它在内存中连续存储相同...

    ArrayList类操作程序实例

    ArrayList是一个基于数组实现的动态列表,可以存储任何类型的数据,并且支持动态扩容。在本实例中,我们将深入探讨ArrayList的常用操作、特性和注意事项。 一、ArrayList的构造方法 ArrayList提供了几种构造方法,...

    ArrayList数组列表[借鉴].pdf

    ArrayList和Vector都是Java集合框架中实现List接口的两种数据结构,它们都允许存储多个元素,但两者在设计和性能上存在显著差异。 1. 同步性: - Vector是线程安全的,这意味着在多线程环境中,多个线程可以同时...

    浅析ArrayList内部实现

    浅析ArrayList内部实现 ArrayList是Java集合框架中的一种常用数据结构,能够存储任意多个对象...ArrayList的内部实现机理是基于数组的,但是通过扩容机理和索引机理,可以实现自由扩展和指定索引位置添加数据的功能。

    ArrayList的实现原理

    总结来说,ArrayList是Java中一个重要的动态数组实现,它的优点在于提供了高效的随机访问和修改,但在插入和删除元素时,特别是当元素位于列表中间时,性能相对较差。在多线程环境下,使用ArrayList需要额外的同步...

    数组:基于数组的问题及其解决方案

    本篇文章将深入探讨基于数组的问题及其解决方案,尤其关注二维数组(2D Arrays)在Java中的应用。 一、一维数组的基础知识 1. 定义与初始化:在Java中,一维数组可以声明为`type[] arrayName;`或`type arrayName[]...

    ArrayList的一个C++类模板实现

    在Java中,ArrayList是基于数组实现的,而在C++中,我们通常使用std::vector来达到类似的效果。然而,对于大规模数据,简单的数组或vector可能会遇到性能瓶颈,特别是在插入和删除元素时,因为它们可能需要移动大量...

    ArrayList共4页.pdf.zip

    ArrayList的设计基于数组,它提供了一个灵活的、可变大小的列表,允许我们在列表的任何位置进行插入和删除操作。ArrayList类继承自AbstractList接口,并实现了Serializable和Cloneable接口,这使得ArrayList对象可以...

    C# ArrayList概述

    ArrayList是.NET框架中System.Collections命名空间下的一个类,它是基于数组实现的数据结构,但与传统的数组相比具有更灵活的功能。ArrayList作为一个动态数组,允许在运行时动态调整其大小,这使得它更适合那些需要...

    ArrayList类[文].pdf

    由于ArrayList是基于数组实现的,因此它支持快速随机访问,但插入和删除元素时可能需要进行数组的扩容或缩容操作,这可能导致一定的性能开销。 1. ArrayList的构造器 - `public ArrayList()`:无参构造器,创建一...

    浅谈Arrays.asList() 和ArrayList类型区别

    如果你需要一个可变的List,并且希望它是基于数组的,你可以自定义一个方法,使用java.util.ArrayList来实现: ```java public static List&lt;String&gt; createArrayList(String[] array) { return new ArrayList...

    ArrayList LinkedList Vector性能测试

    ArrayList是基于数组实现的列表,它维护了一个Object类型的数组,并通过索引来访问元素。由于数组的特性,ArrayList在进行随机读取(get())时具有较高的效率,因为可以通过索引直接访问。但是,当需要插入或删除...

    java数组-基于java实现的环形缓冲数组.zip

    在Java中,我们可以使用数组或ArrayList等集合类来实现环形缓冲数组。环形缓冲数组的设计灵感来自于环形队列,它提供了一种在有限空间内循环存放元素的方式,具有先进先出(FIFO)的特性。 环形缓冲数组的基本概念...

    JAVA提高第十篇 ArrayList深入分析

    ArrayList的主要优点在于它提供了快速的随机访问,但由于它是基于数组实现的,所以在插入和删除元素时,尤其是当元素位置远离当前容量末尾时,性能会相对较差。 一、ArrayList的常见功能 1. 添加元素:ArrayList...

    深入Java集合学习系列(三):ArrayList实现原理

    ArrayList基于动态数组的数据结构,因此它能够提供自动扩容的功能,同时也能够快速地进行随机访问。本篇文档将深入探讨ArrayList的内部实现原理。 首先,ArrayList是Java集合框架中List接口的一个非常重要的实现。...

    java 动态数组的体现

    ArrayList是基于数组实现的,它继承自AbstractList,并实现了List接口。在初始化ArrayList时,它会创建一个默认容量(通常是10)的Object数组。当我们添加元素超过当前容量时,ArrayList会自动进行扩容操作。这个...

Global site tag (gtag.js) - Google Analytics