最近深受轮子哥影响,觉得造一些轮子应该会对自己的技术功底有一定的帮助,就决定先从简单的容器开始实现。废话不多说,就先实现一个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* */@Overridepublic void add(T item) {if (mSize == mCurrentCapacity) {expansion();}mList[mSize] = item;mSize++;}/*** 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置* @param index 要插入的位置* @param item* */@Overridepublic 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* */@Overridepublic void add(T item) {if (mSize == mCurrentCapacity) {expansion();}mList[mSize] = item;mSize++;}/*** 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置* @param index 要插入的位置* @param item* */@Overridepublic 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* */@Overridepublic void set(int index, T item) {if (index < 0 || index >= mSize) {throw new IndexOutOfBoundsException();}mList[index] = item;}/*** 移除指定位置的元素,后面的元素向前移动一位* @param index* */@Overridepublic 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* */@Overridepublic void remove(T item) {for (int i = 0; i < mSize; i++) {if (mList[i].equals(item)) {remove(i);break;}}}/*** 将链表清空,capacity不变* */@Overridepublic void clear() {mList = new Object[mCurrentCapacity];mSize = 0;}/*** 判断是否包含某个元素* @param item* @return true表示有这个元素,false表示没有这个元素* */@Overridepublic boolean contains(T item) {for (int i = 0; i < mSize; i++) {if (mList[i].equals(item)) {return true;}}return false;}/*** 判断链表是否为空* @return boolean* */@Overridepublic boolean isEmpty() {return (mSize == 0) ? true : false;}/*** 获取指定位置的元素* @param index* @return* */@SuppressWarnings("unchecked")@Overridepublic T get(int index) {if (index < 0 || index >= mSize) {throw new IndexOutOfBoundsException();}return (T)mList[index];}/*** 获取特定元素所在的位置。* 如果该链表中存在多个相同的元素,只返回第一个的位置,如果找不到,则返回-1。* @param item* @return int 如果没找到,返回-1* */@Overridepublic int indexOf(T item) {for (int i = 0; i < mSize; i++) {if (mList[i].equals(item)) {return i;}}return -1;}/*** 获取当前链表的长度* @return int* */@Overridepublic 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

转载于:https://www.cnblogs.com/kross/p/4009446.html

实现Java中的ArrayList相关推荐

  1. Java中使用ArrayList的10个示例–教程

    Java中的ArrayList是HashMap之后最常用的集合类. Java ArrayList表示一个可自动调整大小的数组,并用于代替数组. 由于创建数组后我们无法修改数组的大小,因此我们更喜欢在J ...

  2. java arraylist 倒置_如何在Java中反转ArrayList?

    反转ArrayList 为了在Java中反转ArrayList的元素,我们使用Collections类的reverse()方法. reverse()方法reverse()方法在java.util包中可 ...

  3. flex array java_Flex中ArrayCollection 与java中的ArrayList互转

    Flex中ArrayCollection与java中的ArrayList互转 这里说这是Flex用remoting传一个ArrayCollection到后台(后台也用ArrayCollection接参 ...

  4. java中使用ArrayList时防止索引越界

    java中使用ArrayList时防止索引越界 之前看到有人问,使用ArrayList的时候报错,java.lang.IndexOutOfBoundsException: Index: 0, Size ...

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

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

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

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

  7. 如何在Java中同步ArrayList?

    同步ArrayList (Synchronizing ArrayList) In java, there are two ways to synchronize ArrayList, 在Java中,有 ...

  8. 如何在Java中使ArrayList只读?

    使ArrayList只读 (Making ArrayList Read-Only) Given an ArrayList, and we have to make it Read-Only in Ja ...

  9. java中的arrayList(动态数组)与静态数组

    首先,有时用数组时,常把静态数组和动态相混淆,今天来区分一下: 先写一下java中静态数组, 一维数组的声明方式: type var[]; 或type[] var; 声明数组时不能指定其长度(数组中元 ...

最新文章

  1. Python Multiprocessing with PyCUDA
  2. 为什么我们不要 .NET 程序员
  3. Jquery插件之ajaxForm
  4. c++ string类的常用方法_【常用类方法】Object
  5. CSting的GetBuffer()和ReleaseBuffer()
  6. C++ new 解析重载
  7. 在计算机网络GAN代表什么,图解 生成对抗网络GAN 原理 超详解
  8. 推送MobPush-API说明
  9. 2021-09-09321. 拼接最大数 单调栈
  10. 一文解释NDP协议(IPv6邻居发现协议)ICMPv6
  11. 电子行业ERP解决方案
  12. vuecli3代码压缩混淆使用uglifyjs压缩JS
  13. 梦想学院计算机,菲梦学院电脑版
  14. 线性表 :: 顺序存储结构的实现
  15. 1737 - 满足三条件之一需改变的最少字符数 - 枚举 - 贪心
  16. 修改变量名,简单有效地提高代码质量!
  17. J2ME单机游戏在国内已经走到了尽头
  18. 有什么备考软考高级的方法?
  19. @所有中小企业,腾讯安全免费开放基础安全能力,助力构建数字安全免疫力!...
  20. Fisco-Bcos智能合约开发案例----商品溯源

热门文章

  1. 2021年中国工业互联网安全大赛核能行业赛道writeup之Webshell密码
  2. 什么是区块链智能合约?
  3. keras神经网络回归预测_如何使用Keras建立您的第一个神经网络来预测房价
  4. react-dnd-dom_我如何使用react-dnd和react-flip-move构建React游戏
  5. Spring Cloud应用开发(五:API网关服务)
  6. mysql数据库优化命令_MySQL数据库优化总结
  7. 学习Java知识应该注意哪些基础原则
  8. oracle创建DBA角色命令,oracle常用DBA命令
  9. java 根据实体对象生成 增删改的SQL语句 ModelToSQL
  10. CentOS7.4下 VNC Server的搭建和客户端的连接配置