造轮子ArrayList

这篇博客实现一个简单的ArrayList集合.博客里的代码首先根据自己的想法实现,在走不动的情况下会去参考JDK源代码.所以阅读本文,不要抱着跟JDK源码对比的心态.于我个人而言,国庆期间,纯属娱乐.写完打游戏去.

首先写搭建一个架子

public class MyArrayList<E> {
    /*
    * 注意ArrayList的大小是可变的,此处设定的数组大小为默认大小,和每次扩建的大小
    * */

    //默认的数组大小
    private int DEFAULT_CAPACITY = 1;
    //存储元素的数组
    private Object[] elements;

    //当前下标默认值
    private int index = 0;

    //构造一个默认长度的集合
    public MyArrayList() {
        this.elements = new Object[DEFAULT_CAPACITY];
    }

    //构造一个指定长度的集合.
    public MyArrayList(int capacity) {
        this.DEFAULT_CAPACITY = capacity;
        this.elements = new Object[DEFAULT_CAPACITY];
    }

    //添加元素
    public boolean add(E e) {     //设置元素到指定下标位置
        elements[index] = e;     //下标自增
        index++;
        return true;
    }

    //返回当前集合长度
    public int size(){
        return index;
    }
}
public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList();
        myArrayList.add(1);
        System.out.println(myArrayList.size());//1
    }
}

我们再来尝试创建一个指定大小的集合.

public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(0);
        myArrayList.add(1);
        System.out.println(myArrayList.size());
    }
}

这时是会报错的,如果你指定了一个大小为0的集合,那么会造成下标越界.所以我们要对集合进行修改

    //构造一个指定长度的集合.
    public MyArrayList(int capacity) {
        if(capacity>0){
            DEFAULT_CAPACITY = capacity;
        }this.elements = new Object[DEFAULT_CAPACITY ];
    }

如果我们添加元素时,已经超过了数组的下标,也是会报错的.接下来我们要做对数组的扩建.

我们引入另一个临时的数组

  //临时存储元素的数组
    private Object[] tempElements;

并且修改add()方法

    //添加元素
    public boolean add(E e) {
        //现在数组已经满了
        if (index==elements.length){
            tempElements = new Object[elements.length+DEFAULT_CAPACITY];

           //接着我们需要将已经有的元素copy到新数组内
            for(int x=0;x<elements.length;x++){
                tempElements[x] = elements[x];
            }

            //然后我们将老的数组重新创建
            elements = new Object[elements.length+DEFAULT_CAPACITY];
            //最后我们将临时数组的元素,重新放置到新数组中
            for(int x=0;x<tempElements.length;x++){
                elements[x] = tempElements[x];
            }

        }     //设置元素到指定下标位置
        elements[index] = e;     //下标自增
        index++;
        return true;
    }
public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(0);
        myArrayList.add(1);
        myArrayList.add(2);
        myArrayList.add(3);
        myArrayList.add(4);
        System.out.println(myArrayList.size());//4
    }
}

此时我们的集合,则有了自动扩建大小的功能.

接下来我们来实现一个get()

    //获取指定下标的元素
    public E get(int index){
        return (E)elements[index];
    }
public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(3);
        myArrayList.add(1);
        System.out.println(myArrayList.get(0));//1
        System.out.println(myArrayList.get(1));//null
        System.out.println(myArrayList.get(2));//null
        System.out.println(myArrayList.get(3));// java.lang.ArrayIndexOutOfBoundsException: 3
    }
}

这个结果显然不是我们想要的,我们初始化一个大小为3的集合,并且设置1个元素,此时我们只添加一个元素.其他两个为null,原因是我们创建的Object数组,数组中没有元素默认值它就是null,至于下标越界则是正常情况.此时我们应该加一个限制.

    //获取指定下标的元素
    public E get(int index) {
        if (index >= this.index) {
            throw new RuntimeException("您访问了不存在的元素,当前元素个数最大为" + this.index + "");
        }
        return (E) elements[index];
    }

此处抛出一个RuntimeException,运行时异常模仿JDK中,不让用户try,catch异常.

public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(3);
        myArrayList.add(1);
        System.out.println(myArrayList.get(0));//1
        System.out.println(myArrayList.get(1));//您访问了不存在的元素,当前元素个数最大为1
    }
}

同样我们的set()方法也应该具备检验的能力,所以我们将检验的代码抽离出来.

    //获取指定下标的元素
    public E get(int index) {
        check(index);
        return (E) elements[index];
    }

    //替换指定位置的元素
    public E set(int index, E e) {
        check(index);
        E old = (E) elements[index];
        elements[index] = e;
        return old;
    }

    //检验下标是否越界
    private void check(int index) {
        if (index >= this.index) {
            throw new RuntimeException("您访问了不存在的元素,当前元素个数最大为" + this.index + "");
        }
    }
public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(3);
        myArrayList.add(1);
        System.out.println(myArrayList.set(0, 2));//1
        System.out.println(myArrayList.set(1, 2));//您访问了不存在的元素,当前元素个数最大为1
    }
}

实现根据下标删除元素

    //删除指定位置的元素
    public E remove(int index) {
        check(index);
        /*
         * 删除指定下标的元素
         * 1 创建一个新的数组,大小为删除元素后的数组大小.
         * 2 将下标前的数组元素取出
         * 3 将下标后的数组元素取出
         * */
        E removeObj = (E) elements[index];
        Object[] newObj = new Object[this.index];
        for (int i = 0; i < index; i++) {
            newObj[i] = elements[i];
        }
        for (int i = index + 1; i < this.index; i++) {
            newObj[i - 1] = elements[i];
        }
        elements = newObj;

        return removeObj;
    }
public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(3);
        myArrayList.add(1);
        myArrayList.add(2);
        myArrayList.add(3);
        myArrayList.add(4);
        myArrayList.add(5);
        Integer remove = myArrayList.remove(3);
        System.out.println(remove);//4
        System.out.println(myArrayList.get(0));//1
        System.out.println(myArrayList.get(1));//2
        System.out.println(myArrayList.get(2));//3
        System.out.println(myArrayList.get(3));//5

    }
}

至此我们还差最后一个东西,迭代.我们要遍历集合

public class Main {
    public static void main(String[] args) {
        MyArrayList<Integer> myArrayList = new MyArrayList(3);
        myArrayList.add(1);
        myArrayList.add(2);
        myArrayList.add(3);
        myArrayList.add(4);
        myArrayList.add(5);
        for (Object i:myArrayList){
            System.out.println(i);
        }
    }
}

我们要用增强for,但是呢,这个增强for需要集合实现一个接口

public class MyArrayList<E> implements Iterable 

这个接口需要实现一个方法,这个方法需要返回一个Iterator

    @Override
    public Iterator iterator() {
        return new MyIterator();
    }
    class MyIterator implements Iterator{
        @Override
        public boolean hasNext() {
            return false;
        }

        @Override
        public Object next() {
            return null;
        }
    }

这里我们使用一个内部类,这个内部类实现Iterator.下边我们要把这两个方法给实现了.

    class MyIterator implements Iterator{
        int i = 0;
        @Override
        public boolean hasNext() {
            if(index>i){
                return true;
            }
            return false;
        }

        @Override
        public Object next() {
            return elements[i++];
        }
    }

其实呢,这个所谓的迭代器,维护的就是一个指针,hasNext()就是判断有没有下一个元素,那就是判断elements的index下标.每次next()执行就把指针自增1.

到此一个简单的ArrayList就完成了.

最后附录一个完整的代码

import java.util.Iterator;

public class MyArrayList<E> implements Iterable {
    /*
     * 注意ArrayList的大小是可变的,此处设定的数组大小为默认大小,和每次扩建的大小
     * */

    //默认的数组大小
    private int DEFAULT_CAPACITY = 1;

    //存储元素的数组
    private Object[] elements;

    //临时存储元素的数组
    private Object[] tempElements;

    //当前下标默认值
    private int index = 0;

    //构造一个默认长度的集合
    public MyArrayList() {
        this.elements = new Object[DEFAULT_CAPACITY];
    }

    //构造一个指定长度的集合.
    public MyArrayList(int capacity) {
        if (capacity > 0) {
            DEFAULT_CAPACITY = capacity;
        }
        this.elements = new Object[DEFAULT_CAPACITY];
    }

    //添加元素
    public boolean add(E e) {
        //现在数组已经满了
        if (index == elements.length) {
            tempElements = new Object[elements.length + DEFAULT_CAPACITY];

            //接着我们需要将已经有的元素copy到新数组内
            for (int x = 0; x < elements.length; x++) {
                tempElements[x] = elements[x];
            }

            //然后我们将老的数组重新创建
            elements = new Object[elements.length + DEFAULT_CAPACITY];
            //最后我们将临时数组的元素,重新放置到新数组中
            for (int x = 0; x < tempElements.length; x++) {
                elements[x] = tempElements[x];
            }

        }
        //设置元素到数组
        elements[index] = e;
        //将下标自增
        index++;
        return true;
    }

    //返回当前集合长度
    public int size() {
        return index;
    }

    //获取指定下标的元素
    public E get(int index) {
        check(index);
        return (E) elements[index];
    }

    //替换指定位置的元素
    public E set(int index, E e) {
        check(index);
        E old = (E) elements[index];
        elements[index] = e;
        return old;
    }

    //检验下标是否越界
    private void check(int index) {
        if (index >= this.index) {
            throw new RuntimeException("您访问了不存在的元素,当前元素个数最大为" + this.index + "");
        }
    }

    //删除指定位置的元素
    public E remove(int index) {
        check(index);
        /*
         * 删除指定下标的元素
         * 1 创建一个新的数组,大小为删除元素后的数组大小.
         * 2 将下标前的数组元素取出
         * 3 将下标后的数组元素取出
         * */
        E removeObj = (E) elements[index];
        Object[] newObj = new Object[this.index];
        for (int i = 0; i < index; i++) {
            newObj[i] = elements[i];
        }
        for (int i = index + 1; i < this.index; i++) {
            newObj[i - 1] = elements[i];
        }
        elements = newObj;

        return removeObj;
    }

    @Override
    public Iterator iterator() {
        return new MyIterator();
    }

    class MyIterator implements Iterator{
        int i = 0;
        @Override
        public boolean hasNext() {
            if(index>i){
                return true;
            }
            return false;
        }

        @Override
        public Object next() {
            return elements[i++];
        }
    }
}

原文地址:https://www.cnblogs.com/zumengjie/p/11622103.html

时间: 2024-08-01 22:19:14

造轮子ArrayList的相关文章

快速学习C语言四: 造轮子,ArrayList

高级语言里的列表是最常用的数据结构,在C里造个轮子玩玩,C没有泛型,先用int练习. Collection的ADT一般有hasnext,next,add, remove操作,List一般还加了removeat, insert等,然后Stack有push和pop,Queue有enqueue和dequeue.列表有种实现, ArrayList和LinkedList,总体来说ArrayList更常用一些,就先用数组实现个列表. ArrayList在末尾的添加和删除还是挺快的(O(1)),所以当栈来用挺

跟我一起造轮子 手写springmvc

作为java程序员,项目中使用到的主流框架多多少少和spring有关联,在面试的过程难免会问一些spring springmvc spring boot的东西,比如设计模式的使用. 怎么实现springioc 怎么实现springmvc诸如此类的问题,今天我们就来探寻spring mvc的实现,然后自己实现一个简单的spring mvc 一. 了解spring mvc的基本运行流程 ps: 网上一大堆关于springmvc的详细讲解,在这里就不累赘了 小结:spring mvc的核心是Dispa

python 造轮子(一)——序列与字典

虽然说造轮子很少用了,什么底层东西很少写,但是还是很想学扎实,还是好多东西还是的会,没有底层的支持,比较高级的库学起来还是很困难的. 序列的普遍用法: 1 #-*-coding:utf8-*- 2 3 #索引 4 l = [1,2,3,4] 5 t = (1,2,3,4) 6 d = {1:1,2:2,3:3,4:4} 7 8 9 print l[0] 10 print t[0] 11 print d[1] #键索引 12 13 #切片 14 15 print l[0:5] 16 print t

GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便地使用所有版本的Android动作栏的设计模式. 对于Android 4.0及更高版本,ActionBarSherlock可以自动使用本地ActionBar实现,而对于之前没有ActionBar功能的版本,基于Ice Cream Sandwich的自定义动作栏实现将自动围绕布局.能够让开发者轻松开发

我们为什么喜欢重新造轮子

不要重新造轮子,是英文don't re invite wheel的直译.什么是轮子呢,我们一般认为一个通用中间件或框架,和实际业务没有直接关系的代码,就是轮子.这些代码虽然不是实质的业务逻辑,但是在一个项目中往往是担当骨架的作用. 程序员新手的眼中,这些代码就是经验的象征.在很多项目组中,只有经验丰富的老程序员才有机会去设计和选择这些轮子.可以设计可重用的干货代码,也就成为很多对技术有追求的程序员的努力方向.有几年工作经验,在技术上寻求发展的程序员大多都会自己设计和实现一些经典的轮子,这也成为了

Hybrid App Development: 二、关于造轮子以及在Xcode iOS应用开发中加入Cordova

转载请注明出处:http://www.cnblogs.com/xdxer/p/4111552.html [ctrl+左键点击图片可以查看原图] 在上一篇关于Hybrid App Development的文章中,我讨论了一下在iOS下UIWebView的使用方法.但是光使用一个UIWebView所提供的功能还是完全不能满足我们的需求.   关于造轮子的思考: 在UIKit中的UIWebView虽然已经提供了很多功能了,比如JavaScript和Objc之间的通信.但是考虑到一个问题,如果在Hybr

避免重复造轮子的UI自动化测试框架开发

一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览器的基本上底层都是selenium,驱动无线app和浏览器基本是appium.monkey之类的,底层都是基于官方支持的自动化测试框架开发而来,然后上层又做了各种封装 首先在开始计划开发自动化时,第一步是了解目前已有的自动化开发技术,上面说了,最底层的就那几种,根据实际要去测试的业务需求选择合适的自

第27篇 重复造轮子---模拟IIS服务器

在写程序的时候,重复造轮子是程序员的一个大忌,很多人对重复造轮子持有反对的态度,但是我觉得这个造轮子的过程,是对于现有的知识的一个深入的探索的过程,虽然我们不可能把轮子造的那么的完善,对于现在有的东西是一个更好的理解和使用.   当你打开一个网页时,你会看到一个html页面的呈现,当然这是一个完整的Http的请求和响应的过程,无非是对HTML+CSS+JS一个综合的产物,在这个过程中浏览器请求数据的过程中会发出一个有一个格式的字符串(基于http协议生成的http请求报文),服务器在接收这样的一

Js造轮子,基础篇

在js中,只要不是在函数内部声明的函数都是全局变量了,如果代码量大的情况全局变量的污染是非常可怕的,所以需要造轮子声明自己的变量和自己的全局变量和函数方法 一,声明一个对象 先简单的声明一个对象tool={},这样就可以了,这样一个简单的全局对象就弄好了 二,声明方法和变量 这时候定义方法和变量就可以这样了 1 window.tool = {} 2 window.tool.cip = localStorage.cip; 3 4 //url 5 tool.urlHeader = 'http://1