java arraylist 函数_实现Java中的ArrayList

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

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

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

public interface KIList{public voidadd(T item);public void add(intindex, T item);public void set(intindex, T item);public void remove(intindex);public voidremove(T item);public voidclear();public booleancontains(T item);public booleanisEmpty();public T get(intindex);public intindexOf(T item);public intsize();

}

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

第一步:初始化

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

public class KArrayList implements KIList{/**初始化的容量的大小*/

private final static int INIT_CAPACITY = 12;private Object[] mList = null;/**当前的容量*/

private int mCurrentCapacity = 0;/**容器中元素的个数*/

private int mSize = 0;publicKArrayList() {

mList= newObject[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。可以画个图来表示。

0a4053b18f8b9a18bb890600da9d8da4.png

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

0561b5c4d699de724cb13af1c2355f38.png

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

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

/*** 插入一个元素到链表尾部

*@paramitem

**/@Overridepublic voidadd(T item) {if (mSize ==mCurrentCapacity) {

expansion();

}

mList[mSize]=item;

mSize++;

}/*** 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置

*@paramindex 要插入的位置

*@paramitem

**/@Overridepublic void add(intindex, T item) {if (index < 0 || index >= mSize) { //不允许index小于0,或者index >= 数组当前大小

throw newIndexOutOfBoundsException();

}if (mSize ==mCurrentCapacity) {

expansion();

}

Object[] newList= newObject[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 voidexpansion() {

Object[] oldList=mList;

Object[] newList= newObject[getNewCapacity()];

System.arraycopy(oldList,0, newList, 0, oldList.length);

mList=newList;

}/*** 获取新的容量大小

* 当满的时候每次增加当前容量的50%

**/

private intgetNewCapacity() {return mCurrentCapacity = mCurrentCapacity + (mCurrentCapacity >> 1);

}

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

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

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

第三步:其他的方法

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

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

packagekross.java.util;/*** 用数组实现的链表

*@authorkrosshuang(krossford@foxmail.com)

* @update 2014-9-26 18:36:50 第一次创建

* @update 2014-09-28 17:12:46 测试完成

**/

public class KArrayList implements KIList{/**初始化的容量的大小*/

private final static int INIT_CAPACITY = 12;private Object[] mList = null;/**当前的容量*/

private int mCurrentCapacity = 0;/**容器中元素的个数*/

private int mSize = 0;publicKArrayList() {

mList= newObject[INIT_CAPACITY];

mCurrentCapacity=INIT_CAPACITY;

}/*** 插入一个元素到链表尾部

*@paramitem

**/@Overridepublic voidadd(T item) {if (mSize ==mCurrentCapacity) {

expansion();

}

mList[mSize]=item;

mSize++;

}/*** 插入一个元素到指定位置,从插入位置及其后面的元素往后移动一个位置

*@paramindex 要插入的位置

*@paramitem

**/@Overridepublic void add(intindex, T item) {if (index < 0 || index >= mSize) { //不允许index小于0,或者index >= 数组当前大小

throw newIndexOutOfBoundsException();

}if (mSize ==mCurrentCapacity) {

expansion();

}

Object[] newList= newObject[mCurrentCapacity];

System.arraycopy(mList,0, newList, 0, index);

System.arraycopy(mList, index, newList, index+ 1, mSize -index);

newList[index]=item;

mList=newList;

mSize++;

}/*** 更新指定位置的元素

*@paramindex

*@paramitem

**/@Overridepublic void set(intindex, T item) {if (index < 0 || index >=mSize) {throw newIndexOutOfBoundsException();

}

mList[index]=item;

}/*** 移除指定位置的元素,后面的元素向前移动一位

*@paramindex

**/@Overridepublic void remove(intindex) {if (index < 0 || index >=mSize) {throw newIndexOutOfBoundsException();

}

Object[] newList= newObject[mCurrentCapacity];

System.arraycopy(mList,0, newList, 0, index);

System.arraycopy(mList, index+ 1, newList, index, mSize -index);

mList=newList;

mSize--;

}/*** 移除链表中特定的元素。(如果item在链表中有多个,只移除第一个)

*@paramitem

**/@Overridepublic voidremove(T item) {for (int i = 0; i < mSize; i++) {if(mList[i].equals(item)) {

remove(i);break;

}

}

}/*** 将链表清空,capacity不变

**/@Overridepublic voidclear() {

mList= newObject[mCurrentCapacity];

mSize= 0;

}/*** 判断是否包含某个元素

*@paramitem

*@returntrue表示有这个元素,false表示没有这个元素

**/@Overridepublic booleancontains(T item) {for (int i = 0; i < mSize; i++) {if(mList[i].equals(item)) {return true;

}

}return false;

}/*** 判断链表是否为空

*@returnboolean

**/@Overridepublic booleanisEmpty() {return (mSize == 0) ? true : false;

}/*** 获取指定位置的元素

*@paramindex

*@return**/@SuppressWarnings("unchecked")

@Overridepublic T get(intindex) {if (index < 0 || index >=mSize) {throw newIndexOutOfBoundsException();

}return(T)mList[index];

}/*** 获取特定元素所在的位置。

* 如果该链表中存在多个相同的元素,只返回第一个的位置,如果找不到,则返回-1。

*@paramitem

*@returnint 如果没找到,返回-1

**/@Overridepublic intindexOf(T item) {for (int i = 0; i < mSize; i++) {if(mList[i].equals(item)) {returni;

}

}return -1;

}/*** 获取当前链表的长度

*@returnint

**/@Overridepublic intsize() {returnmSize;

}/*** 扩容,当 mSize == mCurrentCapacity 时调用

**/

private voidexpansion() {

Object[] oldList=mList;

Object[] newList= newObject[getNewCapacity()];

System.arraycopy(oldList,0, newList, 0, oldList.length);

mList=newList;

}/*** 获取新的容量大小

* 当满的时候每次增加当前容量的50%

**/

private intgetNewCapacity() {return mCurrentCapacity = mCurrentCapacity + (mCurrentCapacity >> 1);

}public static voidmain(String[] args) {

KArrayList arr = new KArrayList();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的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值