package test;

import java.util.Arrays;
import java.util.Collection; public class MyArrayList<E> { //存储数据
private Object[] values; //已经使用的容量
private int size; //修改次数
private int modCount; /**
* 根据给定容量初始化
* @param initialCapacity
*/
public MyArrayList(int initialCapacity){
if(initialCapacity > ){//按照给定的容量大小初始化数组
values = new Object[initialCapacity];
}else if(initialCapacity == ){
values = new Object[]{};
}else{
throw new IllegalArgumentException("非法初始化容量: "+
initialCapacity);
} } /**
* 默认初始化方法
*/
public MyArrayList(){
this();
} /**
* 用集合初始化一个ArrayList
* @param c
*/
public MyArrayList(Collection c){
values = c.toArray();
if (values.length != ) {
size = values.length;
if (values.getClass() != Object[].class)
values = Arrays.copyOf(values, size, Object[].class);
}else{
values = new Object[]{};
}
}
/**
* 检查是否数组越界了
* @param index
*/
private void rangeCheck(int index) {
if (index >= size || index < )
throw new IndexOutOfBoundsException("数组越界了");
} /**
* 替换原数组中的值,并且将原值返回
* @param index
* @param element
* @return
*/
public E set(int index,E element){
//检查是否越界
rangeCheck(index); E oldValue = (E)values[index];
values[index] = element; return oldValue;
} /**
* 在数组的末尾增加一个元素
* @param element
*/
public void add(E element){
//扩容
ensureCapacity(size+);
values[size++] = element; } /**
* @param index
* @param element
*/
public void add(int index,E element){
//先检查数组的下标是否越界了
rangeCheck(index);
//扩容
ensureCapacity(size+); Object[] oldValues = values;
values[index] = element; //将原数组index后的值拷贝带新数组中
System.arraycopy(oldValues, index, values, index+, size-index); size++; } /**
* 将一个collection中的元素添加到数组末尾
* @param c
*/
public void addAll(Collection c){
Object[] cArray = c.toArray(); //扩容
ensureCapacity(size+cArray.length); //将传入数据拷贝到数组末尾
System.arraycopy(cArray, , values, size, cArray.length); size += cArray.length;
} /**
* 将一个collection中的元素添加到数组的指定位置
* @param index
* @param c
*/
public void addAll(int index,Collection c){
//先检查数组的下标是否越界了
rangeCheck(index);
//扩容
Object[] cArray = c.toArray();
int cLength = cArray.length; ensureCapacity(size+cLength); Object[] oldValues = values;
//将传入数据拷贝到数组指定位置
System.arraycopy(cArray, , values, index, cLength); //将原数组中index以后的数据拷贝过来
System.arraycopy(oldValues, index, values, values.length-, size-index); size += cLength; } /**
* 根据下标获取元素
* @param index
* @return
*/
public E get(int index){
//先检查数组的下标是否越界了
rangeCheck(index); E e = (E) values[index]; return e; } /**
* 根据下标删除元素
* @param index
* @return
*/
public E remove(int index){
//先检查数组的下标是否越界了
rangeCheck(index); E removeValue = (E) values[index]; System.arraycopy(values, index+, values, index, size-index-); //置为空,gc回收
values[size--] = null; return removeValue; } /**
* 根据对象删除
* @param o
*/
public void remove(Object o){
//考虑元素为null的情况
if(null == o){
for (int i = ; i < size; i++) {
if(values[i] == null){
remove(i);
}
} }else{
for (int i = ; i < size; i++) {
if(values[i].equals(o)){
remove(i);
}
} }
} /**
* 扩容方法,保证数组的长度足够
* @param minCapacity
*/
public void ensureCapacity(int minCapacity){
modCount++; int oldCapacity = values.length;
if (minCapacity > oldCapacity) {
//先默认扩容50%+1,如果还是不能满足,则直接用传入的所需容量
int newCapacity = (oldCapacity*)/+;
if(newCapacity < minCapacity){
newCapacity = minCapacity;
} values = Arrays.copyOf(values, newCapacity); }
} public int size(){
return size;
} }

最后再介绍一下ensureCapacity这个方法的想法(从网上看到的):

我们知道ArrayList的内部是采用数组来存储元素的,由于java数组都是定长的,所以这个数组的大小一定是固定的,这个大小就是capacity。我们可以肯定capacity一定是大于或等于ArrayList的size,那么当size不断增加到了要超过capacity的时候,ArrayList就不得不重新创建新的capacity来容纳更多的元素,这时需要首先建立一个更长的数组,将原来的数组中的元素复制到新数组中,再删除原来的数组。可见当ArrayList越来越大时,这种操作的消耗也是越来越大的。
为了减少这种不必要的重建capacity的操作,当我们能肯定ArrayList大致有多大(或者至少会有多大)时,我们可以先让ArrayList把capacity设为我们期望的大小,以避免多余的数组重建。
假设ArrayList自动把capacity设为10,每次重建时将长度递增原来的三分之二,那么当我们需要大约存储50个元素到ArrayList中时,就会大约需要重建数组4次,分别是在增加第11、第17、第26、第39个元素的时候进行的。如果我们一开始就让ArrayList的capacity为50,那么不需要任何数组重建就能完成所有插入操作了。
java允许我们在构造ArrayList的同时指定capacity,如new ArrayList(50),也允许在以后将它设得更大,而增大capacity就是使用ensureCapacity()方法。注意:capacity只能比原来的更大,而不能比原来的更小,否则java会忽略该操作。ArrayList的初始默认capacity为10,所以给capacity指定小于10的整数是毫无意义的。
最后说说ArrayList的size,前面说过,size一定小于等于capactiy,而且更重要的是,访问超过size的位置将抛出异常,尽管这个位置可能没有超过capacity。ensureCapacity()只可能增加capacity,而不会对size有任何影响。要增加size,只能用add()方法。

Java ArrayList的模拟实现的更多相关文章

  1. Mockito:一个强大的用于Java开发的模拟测试框架

    https://blog.csdn.net/zhoudaxia/article/details/33056093 介绍 本文将介绍模拟测试框架Mockito的一些基础概念, 介绍该框架的优点,讲解应用 ...

  2. java.net.URL 模拟用户登录网页并维持session

    java.net.URL 模拟用户登录网页并维持session 半成品,并非完全有用 import java.io.BufferedReader; import java.io.InputStream ...

  3. Java ArrayList、Vector和LinkedList等的差别与用法(转)

    Java ArrayList.Vector和LinkedList等的差别与用法(转) ArrayList 和Vector是采取数组体式格式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,都 ...

  4. 浅析 java ArrayList

    浅析 java ArrayList 简介 容器是java提供的一些列的数据结构,也可以叫语法糖.容器就是用来装在其他类型数据的数据结构. ArrayList是数组列表所以他继承了数组的优缺点.同时他也 ...

  5. java.net.URL 模拟用户登录网页并维持session【转】

    java.net.URL 模拟用户登录网页并维持session 半成品,并非完全有用 import java.io.BufferedReader; import java.io.InputStream ...

  6. Java ArrayList中对象的排序 (Comparable VS Comparator)

    我们通常使用Collections.sort()方法来对一个简单的数据列表排序.但是当ArrayList是由自定义对象组成的,就需要使用comparable或者comparator接口了.在使用这两者 ...

  7. Java ArrayList源码剖析

    转自: Java ArrayList源码剖析 总体介绍 ArrayList实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现.除该类未实现同步外 ...

  8. Java ArrayList 源代码分析

    Java ArrayList 之前曾经参考 数据结构与算法这本书写过ArrayList的demo,本来以为实现起来都差不多,今天抽空看了下jdk中的ArrayList的实现,差距还是很大啊 首先看一下 ...

  9. Java ArrayList trimToSize()

    前几天看了Java ArrayList,没有明白trimToSize()这个方法是什么意思,所以看了一下源码并且debug一下自己的一个例子,明白了其中的含义.贴在这里. ArrayList al = ...

随机推荐

  1. Opencv第三章

    2. 下面这个练习是帮助掌握矩阵类型.创造一个三通道二维矩阵,字节类型,大小为100×100,并设置所有数值为0. a. 在矩阵中使用void cvCircle(CvArr* img, CvPoint ...

  2. Selenium2+python自动化31-生成测试报告【转载】

    前言 最近小伙伴们总有一些测试报告的问题,网上的一些资料生成报告的方法,我试了都不行,完全生成不了,不知道他们是怎么生成的,同样的代码,有待研究. 今天小编写一下可以生成测试报告的方法.个人觉得也是最 ...

  3. Scanner类的个人分析

    Scanner类读取键盘输入(java中Scanner类nextLine()和next()的区别和使用方法&&java 中的Scanner(非常详细不看后悔)): 2017/3/18 ...

  4. Python 进阶 之 闭包变量

    在闭包内访问外部变量的方法有两种: 1:变量前加nonlocal(仅支持Python3) 2:用列表来代替变量.即使是只有一个元素的数组,否则会报错.

  5. MySQL数据库中的Date,DateTime,int,TimeStamp和Time类型的对比

    DATETIME 用在你需要同时包含日期和时间信息的值时.MySQL检索并且以'YYYY-MM-DD HH:MM:SS'格式显示DATETIME值,支持的范围是'1000-01-01 00:00:00 ...

  6. Python 统一动态创建多个model对应的modelForm类(type()函数)

    一.ModelForm的用法 ModelForm对用户提交的数据有验证功能,但比Form要简单的多 from django.forms import ModelForm # 导入ModelFormcl ...

  7. [转载]JQ 选择器大全[<font color=red>强记忆</font>]

    一.基本选择器  选择器  描 述  返回  示例  #id  根据给定id匹配一个元素  单个元素  $("#test") 选取id为test的元素  .class  根据给定类 ...

  8. HDU 6315.Naive Operations-线段树(两棵树合并)(区间单点更新、区间最值、区间求和)+思维 (2018 Multi-University Training Contest 2 1007)

    6315.Naive Operations 题意很好理解,但是因为区间求和求的是向下取整的a[i]/b[i],所以直接分数更新区间是不对的,所以反过来直接当a[i]==b[i]的时候,线段树对应的位置 ...

  9. POJ 2492 A Bug's Life【并查集高级应用+类似食物链】

    Background Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes ...

  10. telnet执行过程及验证远程服务器是否打开

    telnet执行过程: 工作原理: 当你用Telnet登录进入远程计算机系统时,你事实上启动了两个程序,一个 叫Telnet客户程序,它运行在你的本地机上:另一个叫Telnet服务器程序,它运 行在你 ...