[土味]自制ArrayList

自定义一个简单的ArrayList

主要功能

通过java代码实现一个简单的ArrayList数据结构,可以进行增删改查,以及迭代器实现.

代码实现

package com.example.springboot01.util;

import org.thymeleaf.util.StringUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MyArrayList<T> implements Iterable<T>{
    // 容量
    private int capacity = 3;
    // 使用数组实现
    private T[] arr = (T[])new Object[capacity];
    // 元素个数
    private int index = 0;

    /**
     * 新增
     * @param t
     */
    public void add(T t) {
        if(index + 1 > capacity) { // 动态扩容
            capacity = capacity * 2;
            T[] arr2 = (T[])new Object[capacity];
            for(int i=0; i<size(); i++) {
                arr2[i] = arr[i];
            }
            arr = arr2;
        }
        arr[index] = t;
        index++;
    }

    /**
     * 查询
     * @param i
     * @return
     */
    public T get(int i) {
        return arr[i];
    }

    /**
     * 修改
     * @param l
     * @param t
     */
    public void set(int l, T t) {
        if(l + 1 > size()) {
            throw new ArrayIndexOutOfBoundsException("Index: " + l + ", Size: " + size());
        }
        else {
            arr[l] = t;
        }
    }

    /**
     * 删除指定元素
     * @param t
     */
    public void remove(T t) {
        int flag = -1;
        for(int i=0; i < size(); i++) {
            if(t instanceof String) {
                if(StringUtils.equals(arr[i], t)) {
                    flag = i;
                    break;
                }
            }
            else if(t instanceof Integer) {
                if(arr[i] == t) {
                    flag = i;
                    break;
                }
            }
        }

        if(flag > -1) {
            for(int i=flag; i < size(); i++) {
                arr[i] = arr[i+1];
            }
            index--;
        }
    }

    /**
     * 按索引删除
     * @param flag
     */
    public void remove(int flag) {
        if(flag < 0 || flag > size() - 1) {
            throw new IndexOutOfBoundsException("Index: " + flag + ", Size: " + size());
        }
        else {
            for(int i=flag; i < size(); i++) {
                arr[i] = arr[i+1];
            }
            index--;
        }
    }

    /**
     * 获取元素个数
     * @return
     */
    public int size() {
        return index;
    }

    /**
     * 返回自定义的迭代器
     * @return
     */
    @Override
    public Iterator<T> iterator() {
        return new Itr();
    }

    /**
     * 迭代器实现
     */
    private class Itr implements Iterator<T> {
        // 指针位置,初始值为-1,在0的前面,后面每次调用next()方法,往后移动一位
        private int cursor = -1;

        @Override
        public boolean hasNext() {
            if(cursor + 1 < size()) {
                return true;
            }
            else {
                return false;
            }
        }

        @Override
        public T next() {
            // 这边缺少一个判断,cursor是否越界
            return arr[++cursor];
        }

        @Override
        public void remove() {
            if(cursor < 0 || cursor > size() - 1) {
                throw new IndexOutOfBoundsException("Index: " + cursor + ", Size: " + size());
            }
            else {
                for(int i=cursor; i < size(); i++) {
                    arr[i] = arr[i+1];
                }
                cursor--;   // 这是灵魂所在,在删除了一个元素之后,后面的元素会向前补位,所以指针也应该同步向前一步走,不然就有漏网之鱼
                index--;    // 容器中元素数量减一
            }
        }
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        list.add("6");
        list.add("7");
        for(int i=0; i<list.size(); i++) {
            if("7".equals(list.get(i)) == true) {
                list.remove(i);
                i--;
            }
            //System.out.println("ArrayList: " + list.get(i));
        }
        for(int i=0; i<list.size(); i++) {
            System.out.println("ArrayList: " + list.get(i));
        }

        MyArrayList<String> list2 = new MyArrayList<String>();
        //System.out.println("capacity: " + list2.size());
        list2.add("1");
        list2.add("2");
        list2.add("3");
        list2.add("4");
        //System.out.println("capacity: " + list2.size());
/*        list2.add("4");
        list2.add("5");
        list2.add("6");
        list2.add("7");*/
        //System.out.println(list2.size());
        //list2.remove("5");
        //System.out.println(list2.size());
        //list2.add("5");
        //System.out.println(list2.size());
        //list2.remove(0);
        //System.out.println(list2.size());
        Iterator<String> iterator = list2.iterator();
        System.out.println(iterator.next());
        System.out.println(iterator.next());

        System.out.println(iterator.next());



        for(int i=0; i<list2.size(); i++) {
            if("7".equals(list2.get(i)) == false) {
                list2.remove(i);
                i--;
            }
            //System.out.println("MyArrayList: " + list2.get(i));
        }

/*        while(iterator.hasNext()) {
            String item = iterator.next();
            if("7".equals(item) == false) {
                iterator.remove();
            }
        }*/

        for(int i=0; i<list2.size(); i++) {
            System.out.println("MyArrayList: " + list2.get(i));
        }


    }

}


性能测试

这边的性能测试权作参考,没有实用价值.GC工作时,会影响到时间的计算,我这边把GC设置为G1收集器,这样每次GC的时间都差不多,也很短,使用飞行记录jmc.exe测试平均100ms左右.如果使用jdk1.8默认的Parallel Scavenge + Parallel Old收集器,GC时间有长有短,长的将近3秒,短的30ms.
扩容倍数设置为2时,插入并查询一百万条数据大概耗时100ms,一千万条耗时800ms;
扩容倍数设置为3时,一百万条数据耗时50ms,一千万条耗时600ms.
扩容倍数设置为4时,一百万条数据耗时60ms,一千万条500ms
扩容倍数设置为10时,一千万调数据耗时400ms
扩容倍数设置为20时,一千万调数据耗时450ms
扩容倍数设置为100时,一千万调数据耗时450ms
扩容倍数再调大,耗时都没有明显减少,调整数组初始容量对耗时几乎没有影响,所以如果需要性能调优,可以适当减少扩容次数.

代码优化

package com.example.springboot01.util;

import org.junit.Test;

public class MyArrayList<T> {
    // 容量
    private int capacity = 10;
    // 使用数组实现
    private T[] arr = (T[])new Object[capacity];
    // 元素个数
    private int size = 0;

    /**
     * 新增
     * @param t
     */
    public void add(T t) {
        grow();
        arr[size++] = t;
    }

    /**
     * 按索引删除
     * @param flag
     */
    public void remove(int flag) {
        validIndex(flag);
        // 可用System.arraycopy()代替,测试发现两者差不多
        for(int i = flag; i < size() - 1; i++) {
            arr[i] = arr[i+1];
        }
        // 容器中元素数量减一,并将最后位置的值置位null,以便GC回收
        arr[--size] = null;
    }

    /**
     * 删除指定元素
     * @param t 指定元素
     */
    public void remove(T t) {
        int flag = -1;
        for(int i=0; i < size(); i++) {
            if(t instanceof String) {
                if(equals(arr[i], t)) {
                    flag = i;
                    break;
                }
            }
            else if(t instanceof Integer) {
                if(arr[i] == t) {
                    flag = i;
                    break;
                }
            }
        }
        remove(flag);
    }

    /**
     * 动态扩容
     */
    public void grow() {
        // 动态扩容,每次增加原先的一半容量
        if((size + 1) > capacity) {
            capacity = capacity + (capacity >> 1);
            //capacity *= 3;
            T[] arr2 = (T[])new Object[capacity];
            // 可用System.arraycopy()替代,没有直接for循环快
            for(int i=0; i<size(); i++) {
                arr2[i] = arr[i];
            }
            arr = arr2;
        }
    }

    /**
     * 修改
     * @param i 索引
     * @param t 值
     */
    public void set(int i, T t) {
        validIndex(i);
        arr[i] = t;
    }

    /**
     * 查询
     * @param i 索引
     * @return 返回指定下标的元素
     */
    public T get(int i) {
        validIndex(i);
        return arr[i];
    }

    /**
     * 获取元素个数
     * @return 容器中元素的个数
     */
    public int size() {
        return size;
    }

    /**
     * 校验输入的索引
     * @param i 索引id
     */
    public void validIndex(int i) {
        if(i < 0 || i > (size() - 1)) {
            throw new ArrayIndexOutOfBoundsException(indexOutOfBoundsMsg(i));
        }
    }

    /**
     * 索引超出范围提示信息
     * @param i 索引
     * @return  错误提示
     */
    public String indexOutOfBoundsMsg(int i) {
        return "Index: " + i + ", Size: " + size();
    }

    /**
     * 比较两个字符串是否相等
     * @param first 第一个参数
     * @param second 第二个参数
     * @return  是否相等
     */
    public boolean equals(Object first, Object second) {
        if(first == null && second == null) {
            return true;
        }
        else {
            return (first == null || second == null)?false:first.toString().equals(second.toString());
        }
    }

    /**
     * 返回自定义的迭代器
     * @return Itr对象
     */
    //@Override
    public Itr<T> iterator() {
        return new Itr<T>();
    }

    /**
     * 迭代器实现
     */
    private class Itr<E> {
        // 指针位置,初始值为-1,在0的前面,后面每次调用next()方法,往后移动一位
        private int cursor = -1;

        //@Override
        public boolean hasNext() {
            return (cursor + 1) < size();
        }

        //@Override
        public E next() {
            MyArrayList.this.validIndex(++cursor);
            return (E)arr[cursor];
        }

        //@Override
        public void remove() {
            MyArrayList.this.validIndex(cursor);
            // cursor--,这是灵魂所在,在删除了一个元素之后,后面的元素会向前补位,所以指针也应该同步向前一步走,不然就有漏网之鱼
            MyArrayList.this.remove(cursor--);
        }
    }

    @Test
    public void test() {
        long startTime = System.currentTimeMillis();
        MyArrayList<Integer> list2 = new MyArrayList<>();
        for(int i=1; i< 10000000; i++) {
            list2.add(i);
        }
        System.out.println("cost1: " + (System.currentTimeMillis() - startTime) + "ms");
        // 因为是内部类,所以引用时,需要在前面加上外部类
        MyArrayList<Integer>.Itr<Integer> iterator = list2.iterator();
        System.out.println("cost2: " + (System.currentTimeMillis() - startTime) + "ms");
        while(iterator.hasNext()) {
            Integer item = iterator.next();
            /*if(7 != item) {
                iterator.remove();
            }*/
        }

        /*for(int i=0; i<list2.size(); i++) {
            System.out.println("MyArrayList: " + list2.get(i));
        }*/
        System.out.println("cost3: " + (System.currentTimeMillis() - startTime) + "ms");

    }

}

总结

1.如下声明泛型数组是不行的,会报Type parameter ‘T’ cannot be instantiated directly错误

private T[] arr = new T[capacity];

需要这样强制转换一下

private T[] arr = (T[])new Object[capacity];

2.数组的动态扩容是通过新建一个数组来进行的,然后拷贝元素,最后将新数组的引用地址赋值给老数组

arr = arr2;

3.删除指定元素前,需要对元素类型进行判断,才好进行下一步的匹配,删除元素后,后面的元素自动向前补位,容器中元素总数减一.

4.迭代器的实现是通过内部类Itr实现Iterator接口,并重写里面的hasNext(),next(),remove()等方法实现的.

5.迭代器里面的remove()方法成功执行后,需要将指针位置向前移动一位,不然会漏掉刚补位到前面的元素.

6.创建数组后,如果不赋值,虚拟机会自动初始化为对应类型的零值.如String的零值是null,int的零值是0等.

7.ArrayList初始容量是10,每次扩容增加原先容量的一半,即10=>15=>22=>33=>49=>73=>109这样.如果你要存储100个数据,ArrayList会扩容6次.

8.可以不实现Iterator接口,只是使用内部类Itr时需要在前面加上外部类MyArrayList.

9.性能测试和ArrayList差不多,甚至还要快一点.

参考资料

<<数据结构与算法分析>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值