这篇博客实现一个简单的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());//
}
}

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

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());//
}
}

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

接下来我们来实现一个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));//
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));//
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));//
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);//
System.out.println(myArrayList.get(0));//
System.out.println(myArrayList.get(1));//
System.out.println(myArrayList.get(2));//
System.out.println(myArrayList.get(3));// }
}

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

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++];
}
}
}

造轮子ArrayList的更多相关文章

  1. 跟我一起造轮子 手写springmvc

    原创地址:https://www.cnblogs.com/xrog/p/9820168.html 作为java程序员,项目中使用到的主流框架多多少少和spring有关联,在面试的过程难免会问一些spr ...

  2. 别在重复造轮子了,几个值得应用到项目中的 Java 开源库送给你

    我是风筝,公众号「古时的风筝」.文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面.公众号回复『666』获取高清大图. 风筝我作为一个野路子开发者,直到 ...

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

    一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...

  4. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  5. 【疯狂造轮子-iOS】JSON转Model系列之一

    [疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...

  6. h5engine造轮子

    基于学习的造轮子,这是一个最简单,最基础的一个canvas渲染引擎,通过这个引擎架构,可以很快的学习canvas渲染模式! 地址:https://github.com/RichLiu1023/h5en ...

  7. 我为什么还要造轮子?欠踹?Monk.UI表单美化插件诞生记!

    背景 目前市场上有很多表单美化的UI,做的都挺不错,但是他们都有一个共同点,那就是90%以上都是前端工程师开发的,导致我们引入这些UI的时候,很难和程序绑定.所以作为程序员的我,下了一个决定!我要自己 ...

  8. 「iOS造轮子」之UIButton 用Block响应事件

    俗语说 一个不懒的程序员不是好程序员 造轮子,也只是为了以后更好的coding. coding,简易明了的代码更是所有程序员都希望看到的 无论是看自己的代码,还是接手别人的代码 都希望一看都知道这代码 ...

  9. 重复造轮子感悟 – XLinq性能提升心得

    曾经的两座大山 1.EF 刚接触linq那段时间,感觉这家伙好神奇,语法好优美,好厉害.后来经历了EF一些不如意的地方,就想去弥补,既然想弥补,就必须去了解原理.最开始甚至很长一段时间都搞不懂IQue ...

随机推荐

  1. H5生成二维码

    要用H5生成二维码: 1.引入js库,可自行点击链接复制使用 <script type="text/javascript" src="http://static.r ...

  2. 17-REST framework-Request与Response

    1.Django REST framework提供的视图的主要作用 1.控制序列化器的执行(检验,保存,转换数据) 2.控制数据库查询的执行 Request与Response 1.Request RE ...

  3. ElasticStack的入门学习

    Beats,Logstash负责数据收集与处理.相当于ETL(Extract Transform Load).Elasticsearch负责数据存储.查询.分析.Kibana负责数据探索与可视化分析. ...

  4. kafka的主题与消费

    同一个消费者组不能同时消费同一个分区的数据 不同分区可以消费同一组不同消费者 同一个消费者可以同时消费多个topicA的数据 Topic和consumer依赖zookeeper,producer不依赖

  5. HTTP中的2XX状态码

    HTTP状态码分类 1XX --信息,服务器收到请求,需要请求者继续执行操作 2XX--成功,操作被成功接收并处理 3XX--重定向,需要进一步的操作以完成请求 4XX--客户端错误,请求包含语法错误 ...

  6. xml的解析(概述)

    使用java解析xml☆☆☆ 四个类:分别是针对dom和sax解析使用的类   -dom :     DocumentBuilder:解析器类       -这个类是个抽象类,不能new,       ...

  7. JavaScript初探 二 (了解数据)

    JavaScript初探 (二) JavaScript 事件 HTML事件 HTML事件是可以在浏览器或用户做的某些事情 HTML事件的例子: HTML网页完成加载 HTML输入字段被修改 HTML按 ...

  8. SQLi-LABS Page-3 (Stacked injections) Less-38-Less-45

    Less-38 堆叠注入原理简介堆叠注入简介 Stacked injections: 堆叠注入.从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行.而在真实的运用中也是这样的, 我们知道在 ...

  9. [N久以前发布的文章]php 获取yahoo股票csv数据并封闭成为接口输出json数据

    思路 先从yahoo接口获取数据,再定义接口,转化成为json输出.只供卡通网(kt5.cn)使用 stock.php 接口处理代码 <?php header("Content-Typ ...

  10. mssql sqlserver 使用sql脚本剔除数据中的tab、空格、回车等特殊字符的方法分享

    摘要: 在sqlserver开发中,常常有同事反馈无法剔除空格,我们可以通过仔细检查发现,并不是空格字符,而是tab键,如下所示: 解决方法: 对于这些特殊字符的替换,我们需采用字符所对应的ascii ...