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. (十一)__LINE__、__FUNCTION__的使用

    单片机中也可以用__LINE和__FUNCTION__进行异常信息打印,分别代表当前代码行数和当前代码函数名 printf("line:%d\r\n",__LINE__); pri ...

  2. 虚拟机VMware 安装CentOS6.5

    对linux完全小白的情况下,也能依据下面的文章,一步一步安装使用成功! CentOS 6.5 下载http://www.linuxdown.net/CentOS/2014/0928/3371.htm ...

  3. docker常用命令,安装常用实例,一步式安装mysql

    刚来公司,跟公司测试环境项目的服务器,环境是linux Centos7.2  所有的tomcat都挂载在docker容器下,所以也就学习了一些简单的docker指令(学习之前请了解什么是docker, ...

  4. SSH的简单入门体验(Struts2.1+Spring3.1+Hibernate4.1)- 查询系统(下)

    我们继续吧,SSH最大的优点就是实现的系统的松耦合,能够将后台和前台有机的分离开来. 一.目录结构 一个好的程序要有一个好的开始.我们先来看看整个目录结构吧 主要的是三层架构概念,或者说是mvc的概念 ...

  5. SSH的简单入门体验(Struts2.1+Spring3.1+Hibernate4.1)- 查询系统(上)

    所谓SSH,指的是struts+spring+hibernate的一个集成框架,它是目前较流行的一种Web应用程序的开源框架. 集成SSH框架的系统从职责上分为四层:表示层.业务逻辑层.数据持久层和域 ...

  6. brew安装mysql

    1. 安装mysql5.7版本,不指定版本就默认安装最新的,目前最新是8 brew install mysql@5.7 2. 安装完进入/usr/local/Cellar/mysql@5.7目录,可以 ...

  7. [thinkphp] 是如何输出一个页面的

    表面上看,TP输出一个页面很简单:$this->display(); 实际上是怎么回事呢?$this->display(); 这个display()方法是定义在ThinkPHP/Libra ...

  8. [scrapy] spider object has no attribute '_rules'

    这是因为__init__方法没有继承父类 解决办法: # -*- coding:utf-8 -*- from selenium import webdriver from scrapy.contrib ...

  9. 百度之星资格赛 2016 Problem 1004

    本文链接:http://www.cnblogs.com/Ash-ly/p/5494630.html 题意: 熊所居住的 D 国,是一个完全尊重人权的国度.以至于这个国家的所有人命名自己的名字都非常奇怪 ...

  10. luogu P1772 [ZJOI2006]物流运输

    题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪. ...