赞
踩
ArrayList是一个顺序存储的List(表),而List是一种更具体的Collection(集合),所以
上面4点是主要逻辑,后面是细节补充:
下面我将按自己的理解复现上面的流程,不过接口定义远没有官方的完善,只择取了部分方法;同时为和系统库的接口、类相区别,命名前加My。
首先是接口:
public interface MyIterator<T> {
boolean hasNext();
T next();
void remove();
}
public interface MyIterable<T> {
MyIterator<T> myIterator();
}
public interface MyCollection<T> extends MyIterable<T> {
int size();
boolean isEmpty();
void clear();
boolean contain();
boolean add(T x);
boolean remove(T x);
MyIterator<T> myIterator();
}
public interface MyList<T> extends MyCollection<T> { int size(); boolean isEmpty(); void clear(); boolean contain(); boolean add(T x); boolean remove(T x); T get(int idx); T set(int idx, T newVal); int indexOf(T x); void add(int idx, T x); void remove(int idx); MyIterator<T> myIterator(); }
注意,在写出上面的 MyList接口 后,我们将做出做出如下顺序调整:
public interface MyList<T> extends MyCollection<T> { int size(); boolean isEmpty(); void clear(); boolean contain(); boolean add(T x); void add(int idx, T x); // overload boolean remove(T x); void remove(int idx); // overload T get(int idx); int indexOf(T x); T set(int idx, T newVal); MyIterator<T> myIterator(); }
个人根据逻辑和阅读顺序,初步整理了一份个人的 代码书写习惯,如下:
public class A extends B implements C {
Static变量
嵌套类 //即Static内部类
成员变量
构造方法
public的接口方法
接口方法中需要用到的private方法 //放在第一次用到的public方法后
接口方法中需要用到内部类的方法和相应的内部类 //放在最后
public的新方法
新方法中需要用到的private方法 //放在第一次用到的public方法后
新方法中需要用到内部类的方法和相应的内部类 //放在最后
} // of class A
这样的代码不一定最优,因为前面的方法(如override)可能用到后面的方法(该类特有的新方法)来实现。private方法直接写在第一次用到该私有方法的public方法后面。
在设计接口时,则是简单方法在前,复杂的涉及到内部类(返回值、参数也是接口)的方法放最后。在此基础上,再按先后顺序排布,若有新方法是对继承方法的重载则提前。
然后给出MyArrayList类的框架:
public class MyArrayList<T> implements MyList<T> { private static final int DEFAULT_CAPACITY = 10; private int theSize; private T[] theItems; public MyArrayList() {} public MyArrayList(T[] paraArray) {} public int size() {} public boolean isEmpty() {} public void clear() {} public boolean contain(T x) {} public boolean add(T x) {} public void add(int idx, T x) {} public boolean remove(T x) {} public T remove(int idx) {} public T get(int idx) {} public int get(T paraValue) {} public T set(int idx, T newVal) {} public MyIterator<T> myIterator() {} private void doClear() {} private class ArrayListIterator implements MyIterator<T> { private int current = 0; public boolean hasNext() {} public T next() {} public void remove() {} } // Ensure a large enough capacity. public void ensureCapacity(int newCapacity) {} // Trim theItems.length to {@size()}. public void trimToSize() {} public String toString() {} }
我个人将写的接口放在myList项目的myAPI包中,后面的MyArrayList和MyLinkedList都属于myList项目各自独立的包,这样它们都可以用这些接口。
关于toString()方法,补充一点细节:
下面给出2种可行的方法,一种利用数组的get(int),一种利用迭代器的next():
1:
public String toString() {
String s = "empty.";
if (!isEmpty()) {
s = "{ ";
for (int i = 0; i < theSize - 1; i++) {
s += get(i) + ", ";
} // of for i
s += get(theSize - 1) + " }";
} // of if
return s;
} // of toString
2:
public String toString() {
ArrayListIterator itr = (ArrayListIterator) this.myIterator();
String s = "empty.";
if (!isEmpty()) {
s = "{ ";
while (itr.hasNext()) {
s += itr.next() + " ";
} // of while
s += "}";
} // of if
return s;
} // of toString
这里两种都可以用,因为数组的get(int)和迭代器的next()的复杂度都是
O
(
1
)
O(1)
O(1)。但是第一种形式分明更容易理解,还把第二种写出来,是因为:
一方面,任何的迭代过程,比如这里对集合元素的for循环,本质都是一个while循环,while( 有下一个的条件 ) { 对下一个的操作 },for循环中是借助数组的角标 i 直观的表示位置,而迭代器是自己抽象了位置的概念current,for循环的条件被迭代器抽象成boolean hasNext()方法,迭代器的next()方法有2个作用,每次next()后:1是返回当前current位置对应的值,2是current++。因此用迭代器的思想更本质地理解了整个过程。
另一方面,在后面的LinkedList中,get(int)方法的消耗是
O
(
N
)
O(N)
O(N)的,不再是
O
(
1
)
O(1)
O(1),用for循环依然能做,但是消耗将会大大增加,而迭代器的next()则不会。
最后对上述框架进行完善(将省略对成员的注释和一些错误检测),并进行相关测试:
2021/7/21再次进行修订,使用了Java原接口Iterator,没再使用自己的接口,其他不变,这样可以用编译器实现foreach循环,而不需要自己写一个迭代器变量加while循环来改写每次的for each循环。
package myList; import java.util.Iterator; /** * @author CatInCoffee. * @version 1.2 Revised on 2021/7/21. */ public class MyArrayList<T> implements Iterable<T>{ private static final int DEFAULT_CAPACITY = 10; private int theSize; private T[] theItems; // ***************************************************************************************** public MyArrayList() { clear(); } // Of the first constructor public void clear() { theSize = 0; ensureCapacity(DEFAULT_CAPACITY); } // of clear() // O(N) public void ensureCapacity(int newCapacity) { if (newCapacity < theSize) { return; } // if T[] old = theItems; theItems = (T[]) new Object[newCapacity]; for (int i = 0; i < size(); i++) { theItems[i] = old[i]; } // of for i } // of ensureCapacity(int) // O(N) public MyArrayList(T[] paraArray) { theSize = paraArray.length; theItems = (T[]) (new Object[theSize * 2 + 1]); ensureCapacity(DEFAULT_CAPACITY); System.out.println("The length of array used to store data is " + theItems.length + "\n"); for (int i = 0; i < paraArray.length; i++) { theItems[i] = paraArray[i]; } // Of for i } // Of the second constructor // ***************************************************************************************** public int size() { return theSize; } // of size() public boolean isEmpty() { return size() == 0; } // of isEmpty() // O(N) public boolean contain(T x) { return indexOf(x) >= 0; } // of contain(T) // ***************************************************************************************** // O(1) public boolean add(T x) { add(size(), x); return true; } // of add(T) // O(N) public void add(int idx, T x) { if (theItems.length == size()) { ensureCapacity(size() * 2 + 1); } // of if for (int i = theSize; i > idx; i--) { theItems[i] = theItems[i - 1]; } // of for i theItems[idx] = x; theSize++; } // of add(int, T) // O(N) ,因为indexOf是从前往后至idx, 而remove(idx)是从idx往后,相当于1次遍历 public boolean remove(T paraValue) { int idx = indexOf(paraValue); if (idx == -1) { return false; } // of if remove(idx); return true; } // of remove(T) // O(N) public T remove(int idx) { T removedItem = theItems[idx]; for (int i = idx; i < size() - 1; i++) { theItems[i] = theItems[i + 1]; } // of for i theSize--; return removedItem; } // of remove(int idx) // ***************************************************************************************** // O(1) public T get(int idx) { if (idx < 0 || idx >= size()) { throw new ArrayIndexOutOfBoundsException(); } // of if return theItems[idx]; } // of get(int) /** * Return the index of the first occurrence of the specified element in this * list, or -1 if this list does not contain the element. More formally, returns * the lowest index, or -1 if there is no such index. O(N) * * @param paraValue the element to search for * @return the index of the first occurrence of the specified element in this * list, or -1 if this list does not contain the element */ public int indexOf(T paraValue) { int tempPosition = -1; if (paraValue == null) { for (int i = 0; i < theSize; i++) { if (theItems[i] == null) { tempPosition = i; break; } // Of if } // Of for i } else { for (int i = 0; i < theSize; i++) { if (theItems[i].equals(paraValue)) { // ==比较对象地址是否相同,equals常自定义,一般为比较值 tempPosition = i; break; } // Of if } // Of for i } // of if-else return tempPosition; } // of indexOf(T) // O(1) public T set(int idx, T newVal) { if (idx < 0 || idx >= size()) { throw new ArrayIndexOutOfBoundsException(); } // of if T old = theItems[idx]; theItems[idx] = newVal; return old; } // of set(int, T) // ***************************************************************************************** public Iterator<T> iterator() { return new ArrayListIterator(); } // of myIterator() // The time complexities of the methods in the Iterator are all O(1). private class ArrayListIterator implements Iterator<T> { private int indexOfNext = 0; public boolean hasNext() { return indexOfNext < size(); } // of hasNext() public T next() { if (!hasNext()) { throw new java.util.NoSuchElementException(); } // of if return theItems[indexOfNext++]; } // 0f next() public void remove() { MyArrayList.this.remove(--indexOfNext); } // 0f remove() } // of class ArrayListIterator // ***************************************************************************************** // Trim theItems.length to {@size()}. public void trimToSize() { ensureCapacity(size()); }// of trimToSize() public String toString() { String s = "empty."; if (!isEmpty()) { s = "{ "; for (int i = 0; i < theSize - 1; i++) { s += get(i) + ", "; } // of for i s += get(theSize - 1) + " }"; } // of if // ArrayListIterator itr = (ArrayListIterator) this.myIterator(); // String s = "empty."; // if (!isEmpty()) { // s = "{ "; // while (itr.hasNext()) { // s += itr.next() + " "; // } // of while // s += "}"; // } // of if return s; }// of toString() // ***************************************************************************************** public static void main(String[] args) { String[] tempArray = { "1", "2", "3", "4", "5", "6", "7", "f", "c", "d", "i" }; System.out.println("tempArray.length = " + tempArray.length); MyArrayList<String> testArrayList = new MyArrayList<>(tempArray); System.out.println("After being initialized, the list is: " + testArrayList.toString()); String tempValue = "i"; System.out.println( "The judgement that the list contains " + tempValue + " is :" + testArrayList.contain(tempValue)); int tempPosition = testArrayList.indexOf(tempValue); System.out.println("The position of " + tempValue + " is " + tempPosition); tempValue = "e"; System.out.println( "The judgement that the list contains " + tempValue + " is :" + testArrayList.contain(tempValue)); tempPosition = testArrayList.indexOf(tempValue); System.out.println("The position of " + tempValue + " is " + tempPosition + "\n"); tempPosition = 1; tempValue = "g"; testArrayList.add(tempPosition, tempValue); System.out.println("After adding " + tempValue + " to position " + tempPosition + ", the list is: " + testArrayList.toString()); testArrayList.add("j"); System.out.println("After adding j to the end, the list is: " + testArrayList.toString()); tempPosition = 2; tempValue = "h"; testArrayList.set(tempPosition, tempValue); System.out.println("After seting " + tempValue + " at position " + tempPosition + ", the list is: " + testArrayList.toString()); tempPosition = 5; testArrayList.remove(tempPosition); System.out.println("After removing data at position " + tempPosition + ", the list is: " + testArrayList.toString() + "\n"); tempValue = "d"; System.out.println("removing value " + tempValue + " could be : " + testArrayList.remove(tempValue) + ", and the list is: " + testArrayList.toString()); tempValue = "a"; System.out.println("Removing value " + tempValue + " could be : " + testArrayList.remove(tempValue) + ", and the list is: " + testArrayList.toString()); testArrayList.clear(); System.out.println("After the clear() method, the list is: " + testArrayList); }// of main }// of class MyArrayList<T>
结果:
tempArray.length = 11 The length of array used to store data is 23 After being initialized, the list is: { 1, 2, 3, 4, 5, 6, 7, f, c, d, i } The judgement that the list contains i is :true The position of i is 10 The judgement that the list contains e is :false The position of e is -1 After adding g to position 1, the list is: { 1, g, 2, 3, 4, 5, 6, 7, f, c, d, i } After adding j to the end, the list is: { 1, g, 2, 3, 4, 5, 6, 7, f, c, d, i, j } After seting h at position 2, the list is: { 1, g, h, 3, 4, 5, 6, 7, f, c, d, i, j } After removing data at position 5, the list is: { 1, g, h, 3, 4, 6, 7, f, c, d, i, j } removing value d could be : true, and the list is: { 1, g, h, 3, 4, 6, 7, f, c, i, j } Removing value a could be : false, and the list is: { 1, g, h, 3, 4, 6, 7, f, c, i, j } After the clear() method, the list is: empty.
PS:if (condition) 后不加 {} 的第一条语句是 if 的判断执行语句,此后的不再与 if 相关,如
if (false)
System.out.print("1");
System.out.print("2");
等同于
if (false) System.out.print("1");
System.out.print("2");
和
if (false) {
System.out.print("1");
}
System.out.print("2");
出于后面修改代码的安全考虑,还是加上 {} ,免得后面把相同缩减的后续语句当成是 if 的执行语句块。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。