模拟实现ArrayList的部分功能

import java.util.Arrays;

/**
 * 模拟ArrayList的实现
 * @author zhidanfeng
 * @date Sep 25, 2013 2:38:17 PM
 * @packName com.zhi.test1
 * @param <T>
 */
@SuppressWarnings("unchecked")
public class SequenceList<T> {
	
	private final int DEFAULT_SIZE = 6;
	// 保存数组的长度
	private int capacity;
	// 定义一个数组用于保存顺序线性表的元素
	private Object[] elementData;
	// 保存顺序表中元素的当前个数
	private int size = 0;
	
	/**
	 * 以默认长度创建空顺序线性表
	 */
	public SequenceList() {
		capacity = DEFAULT_SIZE;
		elementData = new Object[capacity];
	}
	
	/**
	 * 以一个初始化元素来创建顺序线性表
	 * @param element
	 */
	public SequenceList(T element) {
		this();
		elementData[0] = element;
		size++;
	}
	
	/**
	 * 以指定长度的数组来创建顺序线性表
	 * @param element 指定顺序线性表中第一个元素
	 * @param initSize 指定顺序线性表底层数组的长度
	 */
	public SequenceList(T element, int initSize) {
		capacity = 1;
		// 把capacity大小设定为指定长度的最小的2的n次方
		while(capacity < initSize) {
			capacity <<= 1;
		}
		elementData = new Object[capacity];
		elementData[0] = element;
		size++;
	}
	
	/**
	 * 获取顺序线性表的长度
	 * @return
	 */
	public int length() {
		return size;
	}
	
	/**
	 * 获取指定索引为i处的元素
	 * @param i 索引位置
	 * @return
	 */
	public T get(int i) {
		if(i < 0 || i > size) {
			throw new IndexOutOfBoundsException("线性表索引越界");
		}
		return (T) elementData[i];
	}
	
	/**
	 * 查询指定元素在顺序线性表中位置
	 * @param element 要查询的指定元素
	 * @return
	 */
	public int locate(T element) {
		for(int i = 0; i < size; i++) {
			if(elementData[i].equals(element)) {
				return i;
			}
		}
		return -1;
	}
	
	/**
	 * 在指定索引index处插入指定元素element
	 * @param element 要插入的指定元素
	 * @param index 要插入的索引位置
	 */
	public void insert(T element, int index) {
		
		// 1. 判断插入索引的正确性
		if(index < 0 || index > size) {
			throw new IndexOutOfBoundsException("线性表索引越界");
		}
		// 2. 
		ensureCapacity(size + 1);
		// 3. 将指定索引出后面的元素集体向后移动一格
		System.arraycopy(elementData, index, elementData, index + 1, size - index);
		// 4. 将指定元素插入指定索引index处
		elementData[index] = element;
		// 5. 将顺序线性表长度+1
		size++;
	}
	
	/**
	 * 性能很差
	 * @param minCapacity
	 */
	public void ensureCapacity(int minCapacity) {
		// 如果数组的原有长度小于目前所需的长度,就必须增大所谓的数组长度
		// 但是我们知道数组一旦创建,长度就不可更改,那么只能重新创建一个更大容量的数组
		// 然后将原来的数组拷贝过去
		if(minCapacity > capacity) {
			while(capacity < minCapacity) {
				// 把capacity大小设定为指定长度的最小的2的n次方
				capacity <<= 1;
			}
			// 将旧数组拷贝至一个容量更大的新数组中
			elementData = Arrays.copyOf(elementData, capacity);
		}
	}
	
	/**
	 * 在线性表的开始出添加一个元素
	 * @param element
	 */
	public void add(T element) {
		insert(element, size);
	}
	
	/**
	 * 删除指定索引处的元素
	 * @param index
	 * @return
	 */
	public T delete(int index) {
		if(index < 0 || index > size - 1) {
			throw new IndexOutOfBoundsException("线性表索引越界");
		}
		// 获取要删除的元素
		T oldValue = (T) elementData[index];
		// 要移动的元素个数
		int numMoved = size - index - 1;
		// 当移动的元素大于0才要移动,否则的话就是删除最后一个元素,当然不用移动
		if(numMoved > 0) {
			// src:被复制的数组         srcPos:从第几个元素开始复制
			// dest:要复制到的数组    destPos:从第几个元素开始粘贴l
			// ength:一共需要复制的元素个数
			System.arraycopy(elementData, index + 1, elementData, index, numMoved);
		}
		// 清空最后一个元素,否则的话只是不将引用指向原有的元素对象,
		// 原有的对象仍然占用了空间,得不到释放
		elementData[--size] = null;
		return oldValue;
	}
	
	/**
	 * 移除顺序线性表最后一个元素
	 * @return
	 */
	public T remove() {
		return delete(size - 1);
	}
	
	/**
	 * 判断顺序线性表是否为空
	 * @return
	 */
	public boolean empty() {
		return size == 0;
	}
	
	/**
	 * 清空线性表
	 */
	public void clear() {
		Arrays.fill(elementData, null);
		size = 0;
	}
	
	public String toString() {
		// 空顺序线性表用[]表示
		if(size == 0) {
			return "[]";
		}
		else {
			// 我们输出的最终格式为[xx, xx, xx]
			StringBuilder builder = new StringBuilder("[");
			for (int i = 0; i < size; i++) {
				builder.append(elementData[i].toString() + ", ");
			}
			int len = builder.length();
			// 去除最后面的", ",并添加"]"封口
			return builder.delete(len - 2, len).append("]").toString();
		}
	}
}

创建测试类:SequenceListTest.java

public class SequenceListTest {
	public static void main(String[] args) {
		
		SequenceList<String> list = new SequenceList<String>();
		
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
		
		list.insert("dddd", 1);
		System.out.println(list);
		
		list.delete(2);
		System.out.println(list);
		
		System.out.println("ccc在list中的位置为:" + list.locate("ccc"));
	}
}

测试结果:

[aaa, dddd, bbb, ccc]
[aaa, dddd, ccc]
ccc在list中的位置为:2


最后再介绍一下ensureCapacity这个方法的想法(从网上看到的):

我们知道ArrayList的内部是采用数组来存储元素的,由于java数组都是定长的,所以这个数组的大小一定是固定的,这个大小就是capacity。我们可以肯定capacity一定是大于或等于ArrayList的size,那么当size不断增加到了要超过capacity的时候,ArrayList就不得不重新创建新的capacity来容纳更多的元素,这时需要首先建立一个更长的数组,将原来的数组中的元素复制到新数组中,再删除原来的数组。可见当ArrayList越来越大时,这种操作的消耗也是越来越大的。
为了减少这种不必要的重建capacity的操作,当我们能肯定ArrayList大致有多大(或者至少会有多大)时,我们可以先让ArrayList把capacity设为我们期望的大小,以避免多余的数组重建。
假设ArrayList自动把capacity设为10,每次重建时将长度递增原来的三分之二,那么当我们需要大约存储50个元素到ArrayList中时,就会大约需要重建数组4次,分别是在增加第11、第17、第26、第39个元素的时候进行的。如果我们一开始就让ArrayList的capacity为50,那么不需要任何数组重建就能完成所有插入操作了。
java允许我们在构造ArrayList的同时指定capacity,如new ArrayList(50),也允许在以后将它设得更大,而增大capacity就是使用ensureCapacity()方法。注意:capacity只能比原来的更大,而不能比原来的更小,否则java会忽略该操作。ArrayList的初始默认capacity为10,所以给capacity指定小于10的整数是毫无意义的。
最后说说ArrayList的size,前面说过,size一定小于等于capactiy,而且更重要的是,访问超过size的位置将抛出异常,尽管这个位置可能没有超过capacity。ensureCapacity()只可能增加capacity,而不会对size有任何影响。要增加size,只能用add()方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值