Java数据结构和算法(1)--自定义一个数组类和动态数组类

前言

今天就要离校了,大学生涯也走到了尽头。肯定有很多不舍,不舍的是学校的安逸和美丽的女友。同时也对自己的未来充满着信心,希望自己能够强大起来,保护自己想要保护的人。之前一段时间,在掘金上面看到一篇文章,文章提到了一个思想:学会编程,而不是学会Java,文中提到了自定义一个模仿ArrayList的类,要去实现其中的add,get,remove等方法。同时正好我之前也在看《Java数据结构和算法》这本书,文中第二章也详细讲解了数组,所以自己也动手完成了自定义一个数组内和动态数组类,于是乎就有了这篇文章去温故而知新。


数组

数组是应用最广泛的数组存储结构。
优点:插入快,如果知道下标,可以非常地存取
缺点:查找慢,删除慢,大小固定。


动态数组

Java也提供了顺序结构的动态数组类ArrayList<E>,数组采用的是顺序结构来存储数据,可以有效利用空间,可用于存储大量的数据,数组不适合动态的改变它所存储的数据,如增加,删除一个单元等。由于数组采用顺序结构存储数据,数组获得第n单元中的数据的速度要比链表获得第n单元中的数据快。

写一个数组类

这个数组类肯定有insert(),find(),delete(),display()这些基础方法。
insert():插入一个元素,然后数组长度+1,返回true。

    public void insert(long value){
        a[count]=value;
        count++;
    }

find():根据元素的值,遍历整个数组的元素,找到返回true,没有找到返回false

public boolean find(long searchValue){
        int j;
        
        for(j=0;j<count;j++){
            
            if(a[j]==searchValue){
                break;
            }
        }
        
        if(j==count){
            return false;
        }else{
            return true;
        }
    }

delete():根据元素的值,遍历整个数组的元素,没有找到对应的元素值,返回flase,
找到了该元素存储在数组的位置,让该位置后面的所有元素向前移动一个位置,返回true。

    public boolean delete(long deleteValue){
        
        int j;
        for(j=0;j<count;j++){
            
            if(a[j]==deleteValue){
                
                break;
            }
            
        }
        
        if(j==count){
            
            return false;
        }else{
            
            for(int k=j;k<count;k++){
                
                a[k]=a[k+1];
            }
            
            count--;
            return true;
        }
    }
    

display():遍历整个数组的元素,打印数组。

    public void display(){
        int j;
        for(j=0;j<count;j++){
            System.out.print(a[j]+" ");
        }
        System.out.print("\n");
        
    }

写一个简单的动态数组类

其实这还是比较难的,但是看了一下ArrayList<E>的源码后,写一个带有基本功能的类还是没有问题的。基本方法:add(),remove(),contain(),toString(),toArray(),removeRange(),get()等。

首先是构造器,有2个构造器,分别一个是有参和无参的。有参的构造器需要传入的参数是所需初始化数组的容量大小,如果这个容量大小>0,那么创建一个数组,数组容量大小为传入的参数。如果这个容量大小=0,那么把EMPTY_ELEMENTDATA这个空数组对象赋给elementData这个数组变量。如果这个容器大小<0,那么抛出参数异常。对于无参构造器而言,直接把elementData数组变量引用这个DEFAULT_CAPACITY_ELEMENTDATA数组对象。

public CmazList1(int inititalCapcacity){
        
        if(inititalCapcacity>0){
            
            this.elementData=new Object[inititalCapcacity];
            
        }else if(inititalCapcacity==0){
            this.elementData=EMPTY_ELEMENTDATA;
        }else{
            
            throw new IllegalArgumentException("参数错误");
        }
    }
    
    public CmazList1(){
        
        this.elementData=DEFAULT_CAPACITY_ELEMENTDATA;
    }

contain():调用indexOf()方法,如果indexOf返回的int值大于0就说明此对象在数组中找到对应存储的位置,那么返回true,否则返回false。

public boolean contain(Object object){
        
        return indexOf(object)>=0;
    }

indexOf():通过遍历整个数组元素,判断该数组的某个单元是否包含这个对象,如果找到了这个对象,返回其存储在数组的下标,否则返回-1。

public int indexOf(Object object){
        
        if(object==null){
            
            for(int i=0;i<size;i++){
                
                if(elementData[i]==null){
                    
                    return i;
                }
            }
        }else{
            
            for(int i=0;i<size;i++){
                
                if(elementData[i].equals(object)){
                    
                    return i;
                    
                }
            }
        }
        
        return -1;
    }

add():先调用 ensureCapacityInternal()方法,进行相关扩容等处理操作。然后添加元素,size加1。size用于记录在数组中元素的个数。

public  boolean  add(Object object){
        
        ensureCapacityInternal(size+1);
        
        elementData[size++]=object;
        
        return true;
    }

ensureCapacityInternal():首先看整个英文的意思是确保内部容量。首先要判断这个数组是哪一个构造器初始化的。如果这个数组是无参构造器初始化的,那么这个数组肯定没有设置初始化数组的容量大小,是一个空数组。然后把minCapacity的值在传入的minCapacity的值和默认容器大小的值中取出最大的一个值,即为minCapacity的值。然后调用ensureExplicitCapacity()

private void ensureCapacityInternal(int minCapacity){
        
        
        if(this.elementData==DEFAULT_CAPACITY_ELEMENTDATA){
            
            minCapacity=Math.max(minCapacity, DEFAULT_CAPACITY);
        }
        
        ensureExplicitCapacity(minCapacity);
    }

ensureExplicitCapacity():这个方法进行的操作,去判断是否要进行扩容。如果minCapacity大于数组的长度,说明这个数组需要进行扩容了,因为数组的空间容不下即将添加的元素。接下来调用grow()去进行扩容。

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

grow():进行扩容的操作,扩容后的大小是原来大小的1.5倍。>>1是右移1位,移动后的值是移动前的值的0.5倍。如果扩容后的大小比minCapacity小,那么就把minCapacity的值赋给newCapacity。如果newCapacity的值比我们之前定义的MAX_ARRAY_SIZE还要大的话,那么就调用hugeCapacity()方法。MAX_ARRAY_SIZE我们定义的大小是Integer.MAX_VALUE-8,也就是Integer型数值的最大值-8。补充:原码是直接将一个数值换算成二进制数,但计算机以补码的形式保存所有的整数。一般情况下,不会出现newCapacity>MAX_ARRAY_SIZE的情况。

private void grow(int minCapacity){
        
        int oldCapacity=elementData.length;
        
        int newCapacity=oldCapacity+(oldCapacity>>1);
        
        if(minCapacity-newCapacity>0){
            
            newCapacity=minCapacity;
        }
        
        if(newCapacity-MAX_ARRAY_SIZE>0){
            
            newCapacity=hugeCapacity(minCapacity);
        }
        
        this.elementData=Arrays.copyOf(elementData,newCapacity);
    }

remove():遍历整个数组元素,如果发现该数组包含这个对象,那么调用fastRemove()删除存储此对象的单元。

public boolean  remove(Object object){
        
        if(object==null){
            
            for(int i=0;i<size;i++){
                
                if(elementData[i]==null){
                    
                    fastRemove(i);
                    return true;
                }
            }
        }else{
            
            for(int i=0;i<size;i++){
                
                if(elementData[i].equals(object)){
                    fastRemove(i);      
                    return true;
                }
            }
        }
        
        return false;
        
    }

remove():首先调用get()方法,通过下标得到此单元存储的元素值。这个方法返回就是这个元素值。计算出删除此元素需要挪动的元素个数。如果挪动的元素个数>0,那么调用System.arraycopy()方法,得到一个移动后的数组。elementData[--size]=null,移动后的数组剩余一个存储没有任何元素的单元,那么size--,把没有存储任何元素的单元置为null,通知GC清除无用的内存。
讲解:System.arraycopy(src, srcPos, dest, destPos, length)
src:源数组
srcPos:源数组要复制元素的起始位置
dest:目的数组
destPos:目的数组把复制的元素放置的起始位置
length:复制元素的长度

public E remove(int index){
        rangeCheck(index);
        modCount++;
        E oldValue=get(index);
        int movedNum=size-1-index;
        
        if(movedNum>0){
            
            System.arraycopy(elementData,index+1, elementData, index, movedNum);
        }
        
        elementData[--size]=null;
        
        return oldValue;
    }

removeRange():需要注意的是toIndex指的是要删除的最后一个元素的下一个元素的下标。思路和remove()是一样的,只是remove()是删除一个元素,removeRange()删除的是多个元素。

public void removeRange(int fromIndex,int toIndex){
        
        modCount++;
        
//      int movedNum=size-1-(toIndex-1);
        int movedNum=size-toIndex;
        
        if(movedNum>0){
            
            System.arraycopy(elementData, toIndex, elementData, fromIndex, movedNum);
        }

        int newSize=size+fromIndex-toIndex;
        
        for(int i=newSize;i<size;i++){
            
            elementData[newSize]=null;
        }
        
        size=newSize;
        
    }

源码附上

public class CmazList1<E> {
    
    private static final Object[] DEFAULT_CAPACITY_ELEMENTDATA={};
    
    private static final Object[] EMPTY_ELEMENTDATA={};
    
    private static final int MAX_ARRAY_SIZE=Integer.MAX_VALUE-8;
    
    private static int DEFAULT_CAPACITY=10;
    
    private Object[] elementData;
    
    private int modCount=0;
    
    private int size;
    
    public CmazList1(int inititalCapcacity){
        
        if(inititalCapcacity>0){
            
            this.elementData=new Object[inititalCapcacity];
            
        }else if(inititalCapcacity==0){
            this.elementData=EMPTY_ELEMENTDATA;
        }else{
            
            throw new IllegalArgumentException("参数错误");
        }
    }
    
    public CmazList1(){
        this.elementData=DEFAULT_CAPACITY_ELEMENTDATA;
    }
    
    public boolean contain(Object object){
        
        return indexOf(object)>=0;
    }
    
    public int indexOf(Object object){
        
        if(object==null){
            
            for(int i=0;i<size;i++){
                
                if(elementData[i]==null){
                    
                    return i;
                }
            }
        }else{
            
            for(int i=0;i<size;i++){
                
                if(elementData[i].equals(object)){
                    
                    return i;
                    
                }
            }
        }
        
        return -1;
    }
    
    public  boolean  add(Object object){
        
        ensureCapacityInternal(size+1);
        
        elementData[size++]=object;
        
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity){
        
        
        if(this.elementData==DEFAULT_CAPACITY_ELEMENTDATA){
            
            minCapacity=Math.max(minCapacity, DEFAULT_CAPACITY);
        }
        
        ensureExplicitCapacity(minCapacity);
    }
    
    
    private void ensureExplicitCapacity(int minCapacity){
        
        modCount++;
        
        if(minCapacity-elementData.length>0){
            
            grow(minCapacity);
        }
    }
    
    private void grow(int minCapacity){
        
        int oldCapacity=elementData.length;
        
        int newCapacity=oldCapacity+(oldCapacity>>1);
        
        if(minCapacity-newCapacity>0){
            
            newCapacity=minCapacity;
        }
        
        if(newCapacity-MAX_ARRAY_SIZE>0){
            
            newCapacity=hugeCapacity(minCapacity);
        }
        
        this.elementData=Arrays.copyOf(elementData,newCapacity);
    }
    
    private int hugeCapacity(int minCapacity){
        
        if(minCapacity<0){
            
            throw new OutOfMemoryError();
        }
        return minCapacity>MAX_ARRAY_SIZE?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
    }
    
    public boolean  remove(Object object){
        
        if(object==null){
            
            for(int i=0;i<size;i++){
                
                if(elementData[i]==null){
                    
                    fastRemove(i);
                    return true;
                }
            }
        }else{
            
            for(int i=0;i<size;i++){
                
                if(elementData[i].equals(object)){
                    fastRemove(i);      
                    return true;
                }
            }
        }
        
        return false;
        
    }
    
    
    private  void fastRemove(int index){
        modCount++;
        int movedNum=size-1-index;
        if(movedNum>0){
        
            System.arraycopy(elementData, index+1, elementData, index, movedNum);
        }
        
        elementData[--size]=null;
        
    }
    
    public E remove(int index){
        rangeCheck(index);
        modCount++;
        E oldValue=get(index);
        int movedNum=size-1-index;
        
        if(movedNum>0){
            
            System.arraycopy(elementData,index+1, elementData, index, movedNum);
        }
        
        elementData[--size]=null;
        
        return oldValue;
    }
    
    public E get(int index){
        rangeCheck(index);
        return (E) elementData[index];
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        
        StringBuilder sb=new StringBuilder();
        for(int i=0;i<size;i++){
            sb.append(elementData[i]+" ");
        }
        return sb.toString();
    }
    
    public Object[] toArray(){
        
        return Arrays.copyOf(this.elementData, size);
    }
    
    /**
     * toIndex是指要删除的最后一个元素的下一个元素的下标
     * 比如我要删除下标为5的元素,那么toIndex=6
     * @param fromIndex
     * @param toIndex
     */
    public void removeRange(int fromIndex,int toIndex){
        
        modCount++;
        
//      int movedNum=size-1-(toIndex-1);
        int movedNum=size-toIndex;
        
        if(movedNum>0){
            
            System.arraycopy(elementData, toIndex, elementData, fromIndex, movedNum);
        }
//      
        int newSize=size+fromIndex-toIndex;
        
        for(int i=newSize;i<size;i++){
            
            elementData[newSize]=null;
        }
        
        size=newSize;
        
    }
    
    private  void rangeCheck(int index){
        
        if(index>=size||index<0){
            
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    }
    
    
    private String outOfBoundsMsg(int index){
        return "index:"+index+",size="+size;
    }
}

尾言

数据结构和算法的复利性远比其他时髦技术要多的多。对于程序员来说,不进则死。
打好牢固的基础,一定是没有错的。笨鸟先飞,很适合我。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容