最近深受轮子哥影响,觉得造一些轮子应该会对自己的技术功底有一定的帮助,就决定先从简单的容器开始实现。废话不多说,就先实现一个Java中的ArrayList。

ArrayList是我们在Java中使用非常多的一个类,它是顺序表的数组实现,LinkedList是顺序表的链式实现(自己编的名字,懂就好哈),还有个Vector,它与ArrayList比较像,区别是它是线程安全的。

顺序表应该都有相同的操作,所以我先定义一个接口,描述好顺序表需要哪些操作。代码如下:

public interface KIList<T> {

    public void add(T item);
public void add(int index, T item);
public void set(int index, T item);
public void remove(int index);
public void remove(T item);
public void clear(); public boolean contains(T item);
public boolean isEmpty(); public T get(int index); public int indexOf(T item);
public int size();
}

这些方法的作用,看见方法名就应该可以明白其意义了,因此不必多说了。接下来就开始着手实现吧。

第一步:初始化

首先我们需要一个构造方法,空参数的构造方法,因为是ArrayList是数组实现的顺序表,因此我们需要在一开始就给ArrayList分配一个容量大小。因此构造方法中就需要初始化一个固定大小的数组。于是就有了下面这部分代码。

public class KArrayList<T> implements KIList<T>{

    /** 初始化的容量的大小 */
private final static int INIT_CAPACITY = 12;
private Object[] mList = null; /** 当前的容量 */
private int mCurrentCapacity = 0;
/** 容器中元素的个数 */
private int mSize = 0; public KArrayList() {
mList = new Object[INIT_CAPACITY];
mCurrentCapacity = INIT_CAPACITY;
}
}

首先我定义了一个常量INIT_CAPACITY为12,这个12是看得Java里面的源码,好像原本这个12指的是容量自增的值。INIT_CAPACITY标明我们数组初始化的时候就在内存中开辟了一个多大的空间。

mList指的就是存数据的数组。

mCurrentCapacity指的是当前的容量。这个值并不是一层不变的,显然会随着数组放满的时候扩张一次。

mSize指的就是当前的数组中有效元素的个数了。初始化的值为0。

构造方法中,就直接new一个数组,然后把mCurrentCapacity设置一下就好了。

到这里为止我们就完成了第一步。

第二步:增加元素add

add操作有两个方法,一个是add(T),另一个是add(int, T)。它们的区别是,前者直接在顺序表的结尾插入一个元素,mSize自增1,后者是在指定的位置插入一个元素,指定位置的元素及其之后的元素向后移动一位,mSize自增1。可以画个图来表示。

上图表示的是add(T)的执行过程。

上图是add(int, T)的执行过程。

插入的过程中,需要考虑,当mSize == mCurrentCapacity的时候需要进行一次扩容,另外对于add(int, T),需要对第一个参数index进行判断。最终代码如下:

    /**
* 插入一个元素到链表尾部
* @param item
* */
@Override
public void add(T item) {
if (mSize == mCurrentCapacity) {
expansion();
}
mList[mSize] = item;
mSize++;
} /**
* 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置
* @param index 要插入的位置
* @param item
* */
@Override
public void add(int index, T item) {
if (index < 0 || index >= mSize) { //不允许index小于0,或者index >= 数组当前大小
throw new IndexOutOfBoundsException();
}
if (mSize == mCurrentCapacity) {
expansion();
}
Object[] newList = new Object[mCurrentCapacity];
System.arraycopy(mList, 0, newList, 0, index);
System.arraycopy(mList, index, newList, index + 1, mSize - index);
newList[index] = item;
mList = newList;
mSize++;
}

需要注意的几个地方:

1.expansion()方法是自己写的扩容函数,代码如下:

    /**
* 扩容,当 mSize == mCurrentCapacity 时调用
* */
private void expansion() {
Object[] oldList = mList;
Object[] newList = new Object[getNewCapacity()];
System.arraycopy(oldList, 0, newList, 0, oldList.length);
mList = newList;
} /**
* 获取新的容量大小
* 当满的时候每次增加当前容量的50%
* */
private int getNewCapacity() {
return mCurrentCapacity = mCurrentCapacity + (mCurrentCapacity >> 1);
}

2.注意System.arraycopy()方法,这个是Java提供的一个系统方法,作用就是对数组进行复制操作。

3.注意index的判断,如果越界需要抛出IndexOutOfBoundException。

到此为止,我们就完成了增加操作。

第三步:其他的方法

完成了增加方法后,其他的方法就比较简单或者类似了。直接贴代码就可以看懂了。(顺序表的数组实现本来就是相对比较简单的内容啦)

接下来就是整个类的全部代码。

package kross.java.util;

/**
* 用数组实现的链表
* @author krosshuang(krossford@foxmail.com)
* @update 2014-9-26 18:36:50 第一次创建
* @update 2014-09-28 17:12:46 测试完成
* */
public class KArrayList<T> implements KIList<T>{ /** 初始化的容量的大小 */
private final static int INIT_CAPACITY = 12;
private Object[] mList = null; /** 当前的容量 */
private int mCurrentCapacity = 0;
/** 容器中元素的个数 */
private int mSize = 0; public KArrayList() {
mList = new Object[INIT_CAPACITY];
mCurrentCapacity = INIT_CAPACITY;
} /**
* 插入一个元素到链表尾部
* @param item
* */
@Override
public void add(T item) {
if (mSize == mCurrentCapacity) {
expansion();
}
mList[mSize] = item;
mSize++;
} /**
* 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置
* @param index 要插入的位置
* @param item
* */
@Override
public void add(int index, T item) {
if (index < 0 || index >= mSize) { //不允许index小于0,或者index >= 数组当前大小
throw new IndexOutOfBoundsException();
}
if (mSize == mCurrentCapacity) {
expansion();
}
Object[] newList = new Object[mCurrentCapacity];
System.arraycopy(mList, 0, newList, 0, index);
System.arraycopy(mList, index, newList, index + 1, mSize - index);
newList[index] = item;
mList = newList;
mSize++;
} /**
* 更新指定位置的元素
* @param index
* @param item
* */
@Override
public void set(int index, T item) {
if (index < 0 || index >= mSize) {
throw new IndexOutOfBoundsException();
}
mList[index] = item;
} /**
* 移除指定位置的元素,后面的元素向前移动一位
* @param index
* */
@Override
public void remove(int index) {
if (index < 0 || index >= mSize) {
throw new IndexOutOfBoundsException();
}
Object[] newList = new Object[mCurrentCapacity];
System.arraycopy(mList, 0, newList, 0, index);
System.arraycopy(mList, index + 1, newList, index, mSize - index);
mList = newList;
mSize--;
} /**
* 移除链表中特定的元素。(如果item在链表中有多个,只移除第一个)
* @param item
* */
@Override
public void remove(T item) {
for (int i = 0; i < mSize; i++) {
if (mList[i].equals(item)) {
remove(i);
break;
}
}
} /**
* 将链表清空,capacity不变
* */
@Override
public void clear() {
mList = new Object[mCurrentCapacity];
mSize = 0;
} /**
* 判断是否包含某个元素
* @param item
* @return true表示有这个元素,false表示没有这个元素
* */
@Override
public boolean contains(T item) {
for (int i = 0; i < mSize; i++) {
if (mList[i].equals(item)) {
return true;
}
}
return false;
} /**
* 判断链表是否为空
* @return boolean
* */
@Override
public boolean isEmpty() {
return (mSize == 0) ? true : false;
} /**
* 获取指定位置的元素
* @param index
* @return
* */
@SuppressWarnings("unchecked")
@Override
public T get(int index) {
if (index < 0 || index >= mSize) {
throw new IndexOutOfBoundsException();
}
return (T)mList[index];
} /**
* 获取特定元素所在的位置。
* 如果该链表中存在多个相同的元素,只返回第一个的位置,如果找不到,则返回-1。
* @param item
* @return int 如果没找到,返回-1
* */
@Override
public int indexOf(T item) {
for (int i = 0; i < mSize; i++) {
if (mList[i].equals(item)) {
return i;
}
}
return -1;
} /**
* 获取当前链表的长度
* @return int
* */
@Override
public int size() {
return mSize;
} /**
* 扩容,当 mSize == mCurrentCapacity 时调用
* */
private void expansion() {
Object[] oldList = mList;
Object[] newList = new Object[getNewCapacity()];
System.arraycopy(oldList, 0, newList, 0, oldList.length);
mList = newList;
} /**
* 获取新的容量大小
* 当满的时候每次增加当前容量的50%
* */
private int getNewCapacity() {
return mCurrentCapacity = mCurrentCapacity + (mCurrentCapacity >> 1);
} public static void main(String[] args) {
KArrayList<Integer> arr = new KArrayList<Integer>(); for (int i = 1; i <= 50; i++) {
arr.add(i);
} arr.add(10, 99);
arr.add(0, 99); System.out.println(arr.get(51)); //arr.clear(); //System.out.println(arr.contains(99));
//System.out.println(arr.indexOf(59)); arr.remove(11); arr = null;
}
}

另外,里面main方法是用来做测试的,我自己测的感觉没啥问题。

然后对比一下Java的源码,有一个比较大的困惑就是:

Java中它将数组对象前面加上了transient关键字,这个关键字的作用是:序列化的时候,不会将该字段序列化。也就是说,我在程序中创建了一个[1, 2, 3]的数组,序列化的时候不会序列化这些内容,从文件中反序列化的时候难道[1, 2, 3]就取不出来了吗?

不光是ArrayList,其他的几个容器,数据相关的属性都有这个声明。这一点是比较困惑。mark一下,以后搞明白了再更新上来。

【更新】2014-10-07 17:43:52

关于transient关键字的作用,对于ArrayList来说,是使用数组来存储数据的,但实际的数据只有5个,也就是mSize=5,但数组实际的大小是7(假设),或者更大,那么如果要序列化的时候,就会把那两个空数据的也序列化,无疑就浪费了空间,实际上只有5个有效数据。所以增加了transient字段。而wirteObject方法会自动的将ArrayList中有效数据的部分进行序列化,这样就避免了取出来是null的情况。

原文地址:http://www.cnblogs.com/kross/p/4009446.html

实现Java中的ArrayList的更多相关文章

  1. 深入理解java中的ArrayList和LinkedList

    杂谈最基本数据结构--"线性表": 表结构是一种最基本的数据结构,最常见的实现是数组,几乎在每个程序每一种开发语言中都提供了数组这个顺序存储的线性表结构实现. 什么是线性表? 由0 ...

  2. Java中的ArrayList的初始容量和容量分配

    List接口的大小可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.ArrayList继承于List接口,除继承过来的方法外,还提供一些方法来操作内部用来存储列表的数组的大 ...

  3. java中的ArrayList 使得集合中的对象不重复

    JAVA中的List接口存放的元素是可以重复的,在这个我重写对象里面的equals()方法,让集合里存放的对象不能重复 首先建一个类,在里面的main()方法中实现 list1中存放的是可以重复对象的 ...

  4. java中的ArrayList 、List、LinkedList、Collection关系详解

    一.基础介绍(Set.List.Map) Set(集):集合中的元素不按特定方式排序,并且没有重复对象.他的有些实现类能对集合中的对象按特定方式排序. List(列表):集合中的元素按索引位置排序,可 ...

  5. java中的ArrayList 、List、LinkedList、Collection

    原文地址: http://www.cnblogs.com/liqiu/p/3302607.html 一.基础介绍(Set.List.Map) Set(集):集合中的元素不按特定方式排序,并且没有重复对 ...

  6. 用java中的Arraylist实现电话本系统管理

    大致思路:创建一个电话本条目的类,在主类中实例化.用实例化的对象调用构造参数接收输入值,然后将此对象存入Arraylist的对象中,实现动态添加电话本条目. 该系统具备添加.删除.修改.查询所有和按姓 ...

  7. Java中关于 ArrayList 和 Map 的常用遍历方法 (学习笔记,便于以后查询)

    一.学习ArrayList与Map时,关于常用遍历方法的记录如下:  二.附源码如下: package com.study.in.myself; import java.util.ArrayList; ...

  8. Java中的ArrayList

    ArrayList是所谓的动态数组.用一个小例子: import java.util.ArrayList; import java.util.Iterator; import java.util.Li ...

  9. java中LinkedList ArrayList 数组 HashSet 存储数据测试

    话不多少,直接上代码 import java.text.SimpleDateFormat;import java.util.*; public class testList { public stat ...

随机推荐

  1. zhengruioi 470 区间

    区间 链接 题意:给定n个区间[li,ri].你可以选出任意一些区间,设选出的区间个数是s,[l,r]是这些区间的交,求min(s,r-l+1)的最大值. N≤3×105 分析:首先按照左端点排序,然 ...

  2. [BZOJ4475][JSOI2015]子集选取[推导]

    题意 题目链接 分析 显然可以看成一个位数为 \(n\) 的二进制数然后每一位分开考虑然后求和.最后的答案是 \(w^n\) 的形式. 考虑一个dp. 定义状态 \(f_{i}\) 表示选择了长度为 ...

  3. linux 之 jq

    1.安装 mac 安装: brew install jq centos 安装: yum install jq ubuntu: 安装: apt-get install jq 2.使用 cat test. ...

  4. Async方法死锁的问题 Don't Block on Async Code(转)

    今天调试requet.GetRequestStreamAsync异步方法出现不返回的问题,可能是死锁了.看到老外一篇文章解释了异步方法死锁的问题,懒的翻译,直接搬过来了. http://blog.st ...

  5. CSS3新增特性详解(二)

    上篇博文主要介绍了CSS3新增特性中的静态特性,比如新的选择器.多背景图.阴影.渐变等.本文主要介绍CSS3中新增的动态特性,如过度.动画.变形等. transitian:  -webkit-tran ...

  6. webpack新手入门——配置及安装

    webpack 是一个现代 JavaScript 应用程序的静态模块打包器.当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的 ...

  7. linux上的mysql配置过程

    自己阿里云上的服务器,记录下mysql的配置过程防止后面忘记 1. 首先用apt-get工具安装mysql sudo apt-get install mysql-server sudo apt-get ...

  8. 高可用OpenStack(Queen版)集群-9.Cinder控制节点集群

    参考文档: Install-guide:https://docs.openstack.org/install-guide/ OpenStack High Availability Guide:http ...

  9. 简单安装与使用虚拟环境virtualenv

    安装虚拟环境的命令如下: sudo pip install virtualenv sudo pip install virtualenvwrapper 创建虚拟环境的命令如下: mkvirtualen ...

  10. Tomcat安全管理规范

    s 前言 随着公司内部使用Tomcat作为web应用服务器的规模越来越大,为保证Tomcat的配置安全,防止信息泄露,恶性攻击以及配置的安全规范,特制定此Tomcat安全配置规范. 定位:仅对tomc ...