MyArrayList——实现自己的ArrayList!
注:转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/5965205.html
代码已移植:https://github.com/ygj0930/MyArrayList 大家fork之余随手给我个star呀~
ArrayList是我们常用的集合类之一,其实它的实现机制很简单,底层还是使用了一个传统的Array数组来保存数据的。而动态的实现,只不过是定义了其在长度不足时创建一个更大的数组并把原数组的数据复制过去罢了!而对其保存的数据的增删查该操作,也只不过是封装了一系列最基本的操作数组数据的动作而已。下面,我把自己实现的简略版ArrayList贴上来,供伙伴们参考。
public class MyArrayList<E> {
public transient E[] elements;//底层的泛型数组用来保存元素
/*
这里用transient修饰数组,用transient关键字标记的成员变量不参与序列化过程。
serialization(序列化)提供了一种持久化对象实例的机制,通过序列化可以把对象输出到文档中保存。
而不想某个对象参与序列化,就可以用transient把该对象排除在外。
*/
public int size;//size记录数组当前的有效元素个数
//实现三个构造方法
public MyArrayList(int n) {
if (n > 0) {
this.elements = (E[])new Object[n];//创建容量为n的数组
this.size=0;//一开始数组里个数为0
} else {
throw new IllegalArgumentException("Illegal Capacity: "+n);
}
}
public MyArrayList() {
this.elements = (E[])new Object[10];
this.size=0;
}
public MyArrayList(Collection<Object> c) {
elements = (E[])c.toArray();//将集合转换为数组,数组容量为集合元素个数
if ((size = elements.length) != 0) { //此时数组元素个数等于数组容量
elements = (E[])Arrays.copyOf(elements, size, Object[].class);}
}
//实现容量扩充
public void ensureCapacity(int newCapacity){
int curr=elements.length;
if(newCapacity>curr){
elements=Arrays.copyOf(elements, newCapacity);
}
}
//调整底层数组容量以契合当前元素数量,避免空元素部分太多而浪费内存。size是数组中实际存在的元素个数
public void trimToSize(){
int curr=elements.length;
if(size<curr){
elements=Arrays.copyOf(elements, size);
}
}
//实现下标检查:避免访问越界内存
public void rangeCheck(int requestIndex){
if(requestIndex<0||requestIndex>size){
throw new IndexOutOfBoundsException();
}
}
//实现基本操作:增、删、查、改
//增
//在某个位置插入新元素
public void add(int index,E element){
this.rangeCheck(index);//先检查插入位置的合法性
//再检查当前数组元素个数是否已经达到最大值,即达到数组容量,是,则扩充数组
if(size==elements.length){
ensureCapacity(elements.length*2+1);
}
//将插入位置的原数据以及其后的数据往后移动一位,腾出空间
System.arraycopy(elements, index, elements, index+1, 1);
//插入相应位置,并将数组元素个数加一
elements[index]=element;
++size;
}
//在数组末尾追加一个元素
public void add(E element){
this.add(size, element);//实则等同于在elements[size]处插入元素
}
//将一个集合插入到数组某位置
public void addAll(int index,Collection<? extends E> c){
rangeCheck(index);
//获取插入元素以及个数
E[] newEs=(E[])c.toArray();
int newLength=newEs.length;
//扩充数组容量
ensureCapacity(size+newLength+1);
//计算插入位置以及其后元素的个数,即:需要右移的元素个数
int move=size-index;
//调用System.arraycopy()方法进行数组的大量数据移动操作。
/*
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
方法解读:第一个参数指明数据的来源数组,第二个参数说明数据源的起始位置
第三个参数说明数据去向的目标数组,第四个参数说明数据在目标数组中存放时的起始位置,最后一个参数说明移动的数据长度(个数)。
该函数调用了C语言的memmove()函数,比一般的复制方法的实现效率要高很多,也安全很多,很适合用来批量处理数组。
强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。
*/
//将原数组index~size范围的元素移动,腾出位置
if(move>0){
System.arraycopy(elements, index, elements, index+newLength, move);
}
//将插入数组元素复制到elements数组中腾出的位置
System.arraycopy(newEs,0 , elements, index, newLength);
size+=newLength;//元素个数增加
}
//将一个集合插入到数组末尾
public void addAll(Collection<? extends E> c){
this.addAll(size, c);//相当于在原数组elements[size]处开始,插入一个集合
}
//删
//移除指定位置的元素
public E remove(int index){
rangeCheck(index);
E oldelement=elements[index];
//原位置后面的元素左移
System.arraycopy(elements, index+1, elements, index, size-index-1);
elements[size]=null;//修改原数组最后一个元素位置为空
--size;//修改元素个数
return oldelement;
}
//移除某个值的元素
public boolean remove(E element){
boolean index=false;
for(int i=0;i<size;++i){
if(element.equals(elements[i])){//若找到该值
index=true;//说明有该值,可以执行移除,结果为true。否则,结果false
this.remove(i);//则通过下标来移除
}
}
return index;
}
//移除某个 范围内 的元素(不包括end)
public void removeRange(int start,int end){
int move=size-end;
System.arraycopy(elements, end,elements, start, move);
//修改左移留下的位置为空
for(int i=size-1;i>(size-(end-start)-1);--i){
elements[i]=null;
}
size-=(end-start);
}
//查
//获取某位置的元素
public E get(int index){
rangeCheck(index);
return elements[index];
}
//改
//修改某位置的元素
public void set(int index,E newElement){
rangeCheck(index);
elements[index]=newElement;
}
//转变为普通数组
public E[] toArray()
{
E[] array=(E[]) new Object[size];
System.arraycopy(elements, 0,array,0, size);
return array;
}
public static void main(String[] args) {
MyArrayList<Integer> testMyArrayList=new MyArrayList<Integer>();
System.out.println(testMyArrayList.size);
testMyArrayList.add(1);
testMyArrayList.add(2);
testMyArrayList.add(3);
testMyArrayList.add(4);
testMyArrayList.add(5);
System.out.println(testMyArrayList.size);
System.out.println(testMyArrayList.get(3));
testMyArrayList.removeRange(1, 3);
System.out.println(testMyArrayList.size);
System.out.println(testMyArrayList.get(2));
}
}
MyArrayList——实现自己的ArrayList!的更多相关文章
- 实现MyArrayList类深入理解ArrayList
ArrayList简介 ArrayList是一个动态数组,Array的复杂版本,它提供了动态的增加和减少元素,实现了ICollection和IList接口,灵活的设置数组的大小等好处. MyArray ...
- 设计模式-迭代器模式(Iterator Pattern)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 迭代器模式简介 迭代器相信大部分人都不陌生,java/c++等很多编程语言的容器类都支持迭代器操 ...
- MyArrayList——自己实现ArrayList
注:转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/5965205.html 代码已移植:https://github.com/ygj0930/MyAr ...
- 模仿.NET框架ArrayList写一个自己的动态数组类MyArrayList,揭示foreach实现原理
通过.NET反编译工具可以查看到ArrayList内部的代码,发现ArrayList并非由链表实现,而是由一个不断扩容的数组对象组成. 下面模仿ArrayList写一个自己的MyArrayList. ...
- 深入理解java中的ArrayList和LinkedList
杂谈最基本数据结构--"线性表": 表结构是一种最基本的数据结构,最常见的实现是数组,几乎在每个程序每一种开发语言中都提供了数组这个顺序存储的线性表结构实现. 什么是线性表? 由0 ...
- 01.线性表 ArrayList
public class MyArrayList { //容量 ; //存放数组元素 private object[] _items; //数组大小 private int _size; //元素个数 ...
- ArrayList类的实现
package other; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import ...
- Java中ArrayList的自我实现
对于ArrayList相比大家都很熟悉,它是java中最常用的集合之一.下面就给出它的自我实现的java代码. 需要说明的一点是,它是基于数组创建的.所以它在内存中是顺序存储,对于查找十分的方便. p ...
- List和ArrayList,LinkList的区别
接口 List<E> 是一个接口: ArrayList<E> 是一个类:是一个实现了List接口的类,因此可以List里面定义的所有的方法都实现了. 1.ArrayList是实 ...
随机推荐
- HDU 4741
获得 新的模板了/// 此模板 有线段和线段的最短距离方法,同时包含线段与线段的最短距离:#include<iostream> #include<stdio.h> #inclu ...
- An unexpected exception occurred while creating a change object. see the error log for more details
今天再给Android项目工程中的包重命名时出现了这个错误(之前重命名的时候就没有出现,郁闷): An unexpected exception occurred while creating a c ...
- WCF tcpTrace.exe配置
Web.config <?xml version="1.0" encoding="utf-8"?> <configuration> &l ...
- Xcode各版本官方下载及百度云盘下载, Mac和IOS及Xcode版本历史.
官方下载, 用开发者账户登录,建议用Safari浏览器下载. 官方下载地址: https://developer.apple.com/xcode/downloads/ 百度云盘下载地址: http:/ ...
- JS 中document.URL 和 window.location.href 的区别
实际上,document 和 window 这两个对象的区别已经包含了这个问题的答案. document 表示的是一个文档对象,window 表示一个窗口对象. 一个窗口下面可以有很多的documen ...
- hdu 5444 Elven Postman(根据先序遍历和中序遍历求后序遍历)2015 ACM/ICPC Asia Regional Changchun Online
很坑的一道题,读了半天才读懂题,手忙脚乱的写完(套上模板+修改模板),然后RE到死…… 题意: 题面上告诉了我们这是一棵二叉树,然后告诉了我们它的先序遍历,然后,没了……没了! 反复读题,终于在偶然间 ...
- SPF详解
什么是SPF? 这里的SPF不是防晒指数,而是指Sender Policy Framework.翻译过来就是发信者策略架构,比较拗口,通常都直接称为SPF. SPF是跟DNS相关的一项技术,它的内容写 ...
- JAVA 锁
JAVA 锁 锁的概念 Java中的锁是控制资源访问的一种方式.它弥补了synchronized的可操作性不强的不足. Java的锁都实现了Lock接口.Lock结构定义了锁的基本操作. 函数 解释 ...
- ASP.NET Cookie对象到底是毛啊?(简单小例子)
记得刚接触asp.net的时候,就被几个概念搞的头痛不已,比如Request,Response,Session和Cookie.然后还各种在搜索引擎搜,各种问同事的,但是结果就是自己还是很懵的节奏. 那 ...
- 使用Sunny-grok实现内网转发
Sunny-grok 申请地址:http://www.ngrok.cc ngrok.cfg配置: server_addr: "server.ngrok.cc:4443" auth_ ...