目录
在链接 小肥柴慢慢手写数据结构(C篇)(1-3 线性表 ArrayList 严版教材实现浅析).的最后部分,给出了大佬的git仓,现在就跟着大佬的脚步用java再写一遍(Java和C++都是面向对象的,写起来顺畅很多)
1-1 上代码
- MyArrayList.java
public class MyArrayList<E> {
private static final int DEFAULT_CAPACITY = 20;
private static final int INVALID_INDEX = -1;
private E[] data;
private int size = INVALID_INDEX;
public MyArrayList() {
this(DEFAULT_CAPACITY);
}
public MyArrayList(int capacity) {
data = (E[]) new Object[capacity];
size = 0;
}
public int getSize() {
return size;
}
public int getCapacity() {
return data.length;
}
public boolean isEmpty() {
return size == 0;
}
public int find(E e) {
for (int i = 0; i < size; i++)
if (data[i].equals(e))
return i;
return INVALID_INDEX;
}
public boolean contains(E e) {
for (int i = 0; i < size; i++)
if (data[i].equals(e))
return true;
return false;
}
public void add(int index, E e) {
if (index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
if (index == data.length)
resize(data.length << 1);
for (int i = size - 1; i >= index; i--)
data[i + 1] = data[i];
data[index] = e;
size++;
}
public void addLast(E e) {
add(size, e);
}
public void addFirst(E e) {
add(0, e);
}
public E remove(int index) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("remove failed. Require index >= 0 and index < size.");
E ret = data[index];
for (int i = index; i < size; i++)
data[i] = data[i + 1];
size--;
data[size] = null;
if (size == (data.length >> 2) && (data.length >> 1) != 0)
resize(data.length >> 1);
return ret;
}
public E removeFirst() {
return remove(0);
}
public E removeLast() {
return remove(size - 1);
}
public void removeElement(E e) {
int index = find(e);
if (index != INVALID_INDEX)
remove(index);
}
public E get(int index) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}
public void set(E e, int index) {
if (index < 0 || index >= size)
throw new IllegalArgumentException("Set failed. Index is illegal.");
data[index] = e;
}
private void resize(int newCapacity) {
E[] tmp = data;
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++)
newData[i] = data[i];
data = newData;
tmp = null;
}
@Override
public String toString() {
StringBuilder strb = new StringBuilder();
strb.append("[ ");
for (int i = 0; i < size; i++)
strb.append(data[i] + " ");
strb.append("]");
return strb.toString();
}
}
- Main.java
public class Main {
public static void main(String[] args) {
MyArrayList<Integer> arr = new MyArrayList<>();
for(int i = 0 ; i < 10 ; i ++)
arr.addLast(i);
System.out.println(arr);
arr.add(1, 100);
System.out.println(arr);
arr.addFirst(-1);
System.out.println(arr);
arr.remove(2);
System.out.println(arr);
arr.removeElement(4);
System.out.println(arr);
arr.removeFirst();
System.out.println(arr);
for(int i = 0 ; i < 4 ; i ++){
arr.removeFirst();
System.out.println(arr);
}
}
}
1-2 代码细节
- 由于java中可以使用数组的length方法直接获取当前数组长度,所以不需要维护一个专门的容量(capacity)成员。
- Java在使用前,必须先new得到对象实例,所以也不用太关心是否存在空(NULL)对象的问题,且对于异常情况,直接抛出exception,代码简洁。
- 对于扩表和缩表,都使用一个函数resize完成,把如要修改的容量大小作为入参,简化了函数设计,巧妙。
- 扩表时,依旧采用了两倍方式,在缩表时,采用了lazy方式:当前使用量(用size标记)仅为容量(capacity)的1/4时才会触发,即满足了及时回收空间的需要,也能防止在1/2处反复添加+删除元素导致的频繁resize动作(刘大佬课程里称之为:复杂度的震荡),即反复横跳。这一点很多教材中都不作讨论的,却是很有意思且实用的一个知识点。
- 缩表时有一个size=1的bug,大佬也指出来并修正了。
1-3 源码阅读
网上源码解析的帖子很多,直接学习即可,对sort部分的分析,值得投入时间去看看,但已经属于算法范畴了:
(参考) 《JAVA源码分析》:ArrayList. 这个博主有很多源码解析文章
(参考) java sort排序源码分析(TimSort排序).
(参考)java中List的sort源码解读.
1-4 总结
用java相对于C能快速的实现一个较为完备的数据结构,但对比官方源码实现,还是比较简陋的,但能有就行,而且跟着大佬还能考虑一些小的细节,有收获。