动态循环数组(ArrayList优化)

测试结果分析

动态数组有一个最大的弊端就是浪费内存空间,这个无法避免。
还有一个弊端就是添加删除都需要移动数组元素,最坏情况下要从头移动到尾,也就是n次,致使时间复杂度为O(n) 。
下面这个例子就解决了这个问题,通过判断索引位置,移动元素少的一端,可以至少节约一半的时间。
这个对外使用是无感的,只是内部逻辑实现不同而已。

初始化10,添加8个元素:
CircleArrayList{size=8, head=0, elementData=[1, 2, 3, 4, 5, 6, 7, 8]}
NewRealArrayList{size=8, head=0, elementData=[1, 2, 3, 4, 5, 6, 7, 8, null, null]}
删除第一个元素:
CircleArrayList{size=7, head=1, elementData=[2, 3, 4, 5, 6, 7, 8]}
NewRealArrayList{size=7, head=1, elementData=[null, 2, 3, 4, 5, 6, 7, 8, null, null]}
删除最后一个元素(此时元素有7个索引是6):
CircleArrayList{size=6, head=1, elementData=[2, 3, 4, 5, 6, 7]}
NewRealArrayList{size=6, head=1, elementData=[null, 2, 3, 4, 5, 6, 7, null, null, null]}
结尾添加一个元素:
CircleArrayList{size=7, head=1, elementData=[2, 3, 4, 5, 6, 7, 9]}
NewRealArrayList{size=7, head=1, elementData=[null, 2, 3, 4, 5, 6, 7, 9, null, null]}
删除索引为1的元素(测试删除前半的元素):
CircleArrayList{size=6, head=2, elementData=[2, 4, 5, 6, 7, 9]}
NewRealArrayList{size=6, head=2, elementData=[null, null, 2, 4, 5, 6, 7, 9, null, null]}
删除索引为4的元素(测试删除前半的元素):
CircleArrayList{size=5, head=2, elementData=[2, 4, 5, 6, 9]}
NewRealArrayList{size=5, head=2, elementData=[null, null, 2, 4, 5, 6, 9, null, null, null]}
删除索引为2的元素(测试删除中间的元素):
CircleArrayList{size=4, head=2, elementData=[2, 4, 6, 9]}
NewRealArrayList{size=4, head=2, elementData=[null, null, 2, 4, 6, 9, null, null, null, null]}
在索引为1的位置添加元素(测试添加前半的元素):
CircleArrayList{size=5, head=1, elementData=[2, 331, 4, 6, 9]}
NewRealArrayList{size=5, head=1, elementData=[null, 2, 331, 4, 6, 9, null, null, null, null]}
在索引为3的位置添加元素(测试添加后半的元素):
CircleArrayList{size=6, head=1, elementData=[2, 331, 4, 551, 6, 9]}
NewRealArrayList{size=6, head=1, elementData=[null, 2, 331, 4, 551, 6, 9, null, null, null]}
结尾添加一个元素:
CircleArrayList{size=7, head=1, elementData=[2, 331, 4, 551, 6, 9, 10]}
NewRealArrayList{size=7, head=1, elementData=[null, 2, 331, 4, 551, 6, 9, 10, null, null]}
在索引为3的位置添加元素(测试添加中间的元素):
CircleArrayList{size=8, head=1, elementData=[2, 331, 4, 550, 551, 6, 9, 10]}
NewRealArrayList{size=8, head=1, elementData=[null, 2, 331, 4, 550, 551, 6, 9, 10, null]}
在索引为5的位置添加两个元素(测试绕环):
CircleArrayList{size=10, head=1, elementData=[2, 331, 4, 550, 551, 552, 553, 6, 9, 10]}
NewRealArrayList{size=10, head=1, elementData=[10, 2, 331, 4, 550, 551, 552, 553, 6, 9]}
删除刚才添加的两个元素(测试删除对象):
CircleArrayList{size=8, head=1, elementData=[2, 331, 4, 550, 551, 6, 9, 10]}
NewRealArrayList{size=8, head=1, elementData=[null, 2, 331, 4, 550, 551, 6, 9, 10, null]}
在索引为2的位置添加两个元素(测试前绕环):
CircleArrayList{size=10, head=-1, elementData=[2, 331, 332, 333, 4, 550, 551, 6, 9, 10]}
NewRealArrayList{size=10, head=-1, elementData=[331, 332, 333, 4, 550, 551, 6, 9, 10, 2]}
在索引为5的位置添加一个元素(测试扩容):
CircleArrayList{size=11, head=0, elementData=[2, 331, 332, 333, 4, 441, 550, 551, 6, 9, 10]}
NewRealArrayList{size=11, head=0, elementData=[2, 331, 332, 333, 4, 441, 550, 551, 6, 9, 10, null, null, null, null]}

源代码

package datastruct.linearlist;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;

/**
 * 循环数组
 * 核心在于索引
 * 提升效率
 * 前半添加前半删除扩容缩容处理head
 */
public class HtqCircleArrayList<E> {

    private int head;

    private int size;

    private Object[] elementData;

    private int DEFAULT_CAPACITY = 10;

    private Object[] EMPTY_ELEMENTDATA = {};

    private int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    public HtqCircleArrayList() {
        elementData = EMPTY_ELEMENTDATA;
    }

    public HtqCircleArrayList(int initCapacity) {
        if (initCapacity<0){
            throw new IllegalArgumentException();
        }else if (initCapacity ==0){
            elementData = EMPTY_ELEMENTDATA;
        }else {
            elementData = new Object[initCapacity];
        }
    }

    private int realIndex(int index){
        return (index+head>=elementData.length)?(index+head-elementData.length):
                (index+head<0)?index+head+elementData.length:index+head;
    }

    //error no check
    public void trimToSize(){
        if (size>=elementData.length) return;
        if (size==0){
            elementData = EMPTY_ELEMENTDATA;
            return;
        }
        Object[] objects = new Object[size];
        for (int i=0;i<size;i++){
            objects[i] = elementData[realIndex(i)];
            elementData[realIndex(i)] = null;
        }
        elementData = objects;
    }

    public int size(){
        return size;
    }

    public boolean isEmpty(){
        return size==0;
    }

    public boolean contins(Object o){
        return indexOf(o)!=-1;
    }

    //error 0->o
    private int indexOf(Object o) {
        for (int i=0;i<size;i++){
            if (Objects.equals(elementData[realIndex(i)],o)){
                return i;
            }
        }
        return -1;
    }

    private int lastIndexOf(Object o) {
        for (int i=size-1;i>=0;i--){
            if (Objects.equals(elementData[realIndex(i)],0)){
                return i;
            }
        }
        return -1;
    }

    public void clear(){
        for (int i=0;i<size;i++){
            elementData[realIndex(i)] = null;
        }
        saveCapacity(size);
        size = 0;
    }

    public Object[] toArray(){
        Object[] objects = new Object[size];
        for (int i=0;i<size;i++){
            objects[i] = elementData[realIndex(i)];
        }
        return objects;
    }

    public <T> T[] toArray(T[] a){
        if(a.length<size){
            a = (T[])Array.newInstance(a.getClass().getComponentType(),size);
        }

        Object[] o = a;

        for (int i=0;i<size;i++){
            o[i] = elementData[realIndex(i)];
        }

        if (a.length<size){
            a[size] = null;
        }
        return a;
    }

    //no Comparetor
    public void sort(Comparator<? super  E> comparator){
        trimToSize();
        Arrays.sort((E[])elementData,0,size,comparator);
    }

    public void add(E e){
        ensureCapacityInternal(size+1);

        elementData[realIndex(size++)] = e;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculate(minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        if (minCapacity>elementData.length){
            grow(minCapacity);
        }
    }

    //no deal head
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + oldCapacity>>1;

        if (newCapacity<minCapacity){
            newCapacity = minCapacity;
        }

        if (newCapacity>MAX_ARRAY_SIZE){
            newCapacity = (minCapacity>MAX_ARRAY_SIZE)?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
        }

        Object[] objects = new Object[newCapacity];
        for (int i=0;i<size;i++){
            objects[i] = elementData[realIndex(i)];
        }
        elementData = objects;
        head = 0;
    }

    private int calculate(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA){
            minCapacity = Math.max(minCapacity,DEFAULT_CAPACITY);
        }
        return minCapacity;
    }

    //error no ensureCapacityInternal()
    //i>=index no deal index
    public void add(int index,E e){
        rangeCheckForAdd(index);
        ensureCapacityInternal(size+1);
        if (index<(size>>1)){
            for (int i=0;i<index;i++){
                elementData[realIndex(i-1)] = elementData[realIndex(i)];
            }
            head = head-1;
        }else {
            for (int i=size-1;i>=index;i--){
                elementData[realIndex(i+1)] = elementData[realIndex(i)];
            }
        }
        elementData[realIndex(index)] = e;
        size++;
    }

    @Override public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("CircleArrayList{size=").append(size)
                .append(", head=").append(head)
                .append(", elementData=[");
        for (int i=0;i<size;i++){
            if (i==0){
                stringBuilder.append(elementData(i));
                continue;
            }
            stringBuilder.append(", "+elementData(i));
        }

        stringBuilder.append("]}");
        return stringBuilder.toString();
    }

    public String toRealString() {
        return "NewRealArrayList{" +
                "size=" + size +
                ", head=" + head +
                ", elementData=" + Arrays.toString(elementData) +
                '}';
    }

    private void rangeCheckForAdd(int index) {
        if (index<0||index>size)
            throw new IndexOutOfBoundsException();
    }

    private void rangeCheck(int index) {
        if (index<0||index>=size)
            throw new IndexOutOfBoundsException();
    }

    public E remove(int index){
        rangeCheck(index);
        E e = elementData(index);

        if (index<(size>>1)){
            for (int i=index-1;i>=0;i--){
                elementData[realIndex(i+1)] = elementData[realIndex(i)];
            }
            elementData[head] = null;
            head = head+1;
        }else {
            for (int i=index+1;i<size;i++){
                elementData[realIndex(i-1)] = elementData[realIndex(i)];
            }
            elementData[realIndex(size-1)] = null;
        }
        size--;
        saveCapacity(size);
        return e;
    }

    //error no deal head
    private void saveCapacity(int size) {
        if (size<(elementData.length>>1)){
            Object[] objects = new Object[elementData.length>>1];
            for (int i=0;i<size;i++){
                objects[i] = elementData[realIndex(i)];
                elementData[realIndex(i)] = null;
            }
            elementData = objects;
            head=0;
        }
    }

    private E elementData(int index){
        return (E)elementData[realIndex(index)];
    }

    public E remove(E e){
        int i = indexOf(e);
        return remove(i);
    }

    public E set(int index,E e){
        E oe = elementData(index);
        elementData[realIndex(index)] = e;
        return oe;
    }

    public E get(int index){
        return elementData(index);
    }
}

测试类

package datastructure.linearlist;

public class CircleArrayListTest {

    public static void main(String[] args) {

        HtqCircleArrayList circleArrayList = new HtqCircleArrayList(10);
        System.out.println("初始化10,添加8个元素:");
        circleArrayList.add(1);
        circleArrayList.add(2);
        circleArrayList.add(3);
        circleArrayList.add(4);
        circleArrayList.add(5);
        circleArrayList.add(6);
        circleArrayList.add(7);
        circleArrayList.add(8);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("删除第一个元素:");
        circleArrayList.remove(0);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("删除最后一个元素(此时元素有7个索引是6):");
        circleArrayList.remove(6);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("结尾添加一个元素:");
        circleArrayList.add(9);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("删除索引为1的元素(测试删除前半的元素):");
        circleArrayList.remove(1);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("删除索引为4的元素(测试删除前半的元素):");
        circleArrayList.remove(4);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("删除索引为2的元素(测试删除中间的元素):");
        circleArrayList.remove(2);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("在索引为1的位置添加元素(测试添加前半的元素):");
        circleArrayList.add(1,331);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("在索引为3的位置添加元素(测试添加后半的元素):");
        circleArrayList.add(3,551);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("结尾添加一个元素:");
        circleArrayList.add(10);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("在索引为3的位置添加元素(测试添加中间的元素):");
        circleArrayList.add(3,550);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("在索引为5的位置添加两个元素(测试绕环):");
        circleArrayList.add(5,553);
        circleArrayList.add(5,552);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("删除刚才添加的两个元素(测试删除对象):");
        circleArrayList.remove((Object)553);
        circleArrayList.remove((Object)552);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
        System.out.println("在索引为2的位置添加两个元素(测试前绕环):");
        circleArrayList.add(2,333);
        circleArrayList.add(2,332);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());

        System.out.println("在索引为5的位置添加一个元素(测试扩容):");
        circleArrayList.add(5,441);
        System.out.println(circleArrayList);
        System.out.println(circleArrayList.toRealString());
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值