模拟实现ArrayList的部分功能

来源:互联网 发布:js 获取android版本号 编辑:程序博客网 时间:2024/06/03 06:32
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. 将顺序线性表长度+1size++;}/** * 性能很差 * @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()方法。

原创粉丝点击