起因
前段时间在看一本书码农翻身。这本书很不错,推荐阅读!(真不是打广告,好书要分享)。老实说,在此之前,这是一个公众号(好吧,现在也是),里面写了很多通俗易懂的文章,没想到最近居然出书了,赶快买了一本,一通畅读之后,在书中的其中一章看到了一些有意思的事情。
大意就是,他写了一个List的接口,里面有add()方法,size()方法等等一系列ArrayList的常用方法,然后让面试者去实现这个类,也就是让我们实现ArrayList啊,然后书中还说,他面试了很多人,但是能够实现的不错的人没多少。
接受挑战!
可能实现的很水,也许代码质量远不如各种大神们实现的好,但是至少也实现了不是吗,老实说,在实现完毕后,对ArrayList好像了解的也更深刻了,不过没有细细研读Java自身实现的ArrayList,因为我考虑的只有实现,并没有为了以后要拓展这个类中的功能预留一些的接口或者什么= =。
开撸
首先我们先来撸一个接口,为了不和Java里面的List接口重名,就叫IList
吧,思考了一下常用功能,最终长这样:
public interface IList<T> {
void add(T t);
void addAll(IList<T> tiList);
void remove(int i);
T get(int index);
int size();
void clear();
boolean constain(T t);
}
功能就这些,也不打算多写,相信你们看方法名字也知道这里每个方法是干啥的吧。不过跟Java实现的List接口差别好像还不是一星半点- -!
分析
好吧,现在的问题就是,拿什么来存储我们的数据,跟集合类似的,也就只有数组最合适了,所以自然而然的,Object[]
成为了我们实现ArrayList的得力助手,但是数组的长度是固定的啊,这数组~,看来只好每次要增删数据的时候,对数组的长度更新了。
所以,IList
中的每个方法的具体实现思路就是这样的:
void add(T t);
实现方式:克隆一份原数组,新数组容量+1,将原数组的值一一赋值到新数组中,将要添加的值,放到新数组的末尾void addAll(IList<T> tiList);
实现方式:跟add(T t)
类似,克隆一份原数组,新数组容量+tiList.size()个大小,将原数组的值一一赋值到新数组中,将要添加的值,放到新数组多加的位置上void remove(int i);
实现方式:克隆一份原数组,新数组容量-1,将原数组中的值一一赋值到新数组中,当添加到第i
个的时候,做一下判断,少添加要被去除的值T get(int index);
实现方式:直接返回数组对应的值int size();
实现方式:返回数组长度void clear();
实现方式:清空数组boolean constain(T t);
实现方式:遍历一遍数组,查看是否包含t
所以整个代码的样子就是这样的:
/**
* 模仿ArrayList
* @param <T> 可以添加的元素
*/
public class SimpleList<T> implements IList<T> {
/**
* 用来记录数据的数组
*/
private Object[] es;
/**
* 默认内容(空)
*/
private Object[] defaultArr = {};
public SimpleList() {
es = defaultArr;
}
/**
* 新增元素
*
* @param t 被增加的元素
*/
@Override
public void add(T t) {
es = expandArr(1);
es[es.length - 1] = t;
}
/**
* 将指定集合中的元素全部添加到自己身上
*
* @param list
*/
@Override
public void addAll(IList<T> list) {
int ori = es.length;
es = expandArr(list.size());
for (int i = ori; i < es.length; i++) {
es[i] = list.get(i - ori);
}
}
/**
* 移除指定位置的元素
*
* @param i 位置
*/
@Override
public void remove(int i) {
if (i < size()) {
Object[] clone = es.clone();
es = new Object[clone.length - 1];
boolean isJump = false;
for (int j = 0; j < clone.length; j++) {
if (j == i) {
isJump = true;
continue;
}
if (isJump) {
es[j - 1] = clone[j];
} else {
es[j] = clone[j];
}
}
} else {
throw new RuntimeException("ArrlistOutOfIndex exception: size = " + size() + ", index = " + i);
}
}
/**
* 获取某个元素
*
* @param index 指定位置
* @return 具体元素
*/
@Override
public T get(int index) {
if (index < size()) {
return (T) es[index];
}
throw new RuntimeException("outOfIndexException: index = " + index + ", size = " + size());
}
/**
* 得到集合元素的数量
*
* @return 元素的数量
*/
@Override
public int size() {
return es.length;
}
/**
* 清空所有元素
*/
@Override
public void clear() {
es = defaultArr;
}
/**
* 判断本集合是否存在该集合
*
* @param o
* @return
*/
@Override
public boolean constain(Object o) {
for (int i = 0; i < size(); i++) {
if (es[i].equals(o)) {
return true;
}
}
return false;
}
/**
* 在原数组基础上,"扩大"数组的大小
*
* @param count 扩大的尺寸
* @return 已经扩大的数组,并且该数组上有自己的元素
*/
private Object[] expandArr(int count) {
Object[] clone = es.clone();
es = new Object[es.length + count];
for (int i = 0; i < clone.length; i++) {
es[i] = clone[i];
}
return es;
}
}
测试
既然都实现好了,我们不妨来试试?
public class Main {
public static void main(String[] args) {
// 新增数据
SimpleList<String> simpleList1 = new SimpleList<>();
simpleList1.add("Str1");
simpleList1.add("Str2");
simpleList1.add("Str3");
show("simpleList1", simpleList1);
System.out.println("simpleList1.size = " + simpleList1.size());
// 移除数据
System.out.println("移除第index为2的数据(从0开始,所以实际移除的是第三个数据)");
simpleList1.remove(2);
show("移除后的simpleList1", simpleList1);
// 批量新增数据
SimpleList<String> simpleList2 = new SimpleList<>();
simpleList2.add("a1");
simpleList2.add("a2");
simpleList2.add("a3");
simpleList2.addAll(simpleList1);
show("simpleList2", simpleList2);
// 是否包含某数据
System.out.println("simpleList2是否包含a2:" + simpleList2.constain("a2"));
System.out.println("simpleList2是否包含a9:" + simpleList2.constain("a9"));
// 清空数据
simpleList2.clear();
System.out.println("清理后:simpleList2.size() = " + simpleList2.size());
}
private static void show(String name, SimpleList<String> list) {
System.out.print(name + ":");
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
System.out.println();
}
}
运行结果:
simpleList1:Str1 Str2 Str3
simpleList1.size = 3
移除第index为2的数据(从0开始,所以实际移除的是第三个数据)
移除后的simpleList1:Str1 Str2
simpleList2:a1 a2 a3 Str1 Str2
simpleList2是否包含a2:true
simpleList2是否包含a9:false
清理后:simpleList2.size() = 0
毫无问题,这里我要提一个单词:Nice!
最后
其实自己手动实现一个ArrayList也挺有趣的,ArrayList的结构和数组类似,仿佛是在排列成一排的空格子里面放东西,是不是有点像打麻将的时候,大家面前的麻将。麻将都是排成一排的,可以随时从中间抽一个麻将打出去,也可以拿一个麻将进来(好像没有实现指定插入位置,这个问题就交给你们啦!),话说发明ArrayList的人,是不是打麻将的时候找到的灵感= =。