List的实现
ArrayList的实现
我们在这里实现ArrayList泛型类,为了避免与类库中的类相混合,这里将我们的类称为MyArrayList。其中主要包括以下细节:
- MyArrayList将保持数组、数组容量,以及存储在MyArrayList中的当前项数。
- MyArrayList将提供一种机制来改变基础数组的容量,通过获得一个新数组,将老数组拷贝到新数组中来改变数组容量。
- MyArrayList将提供set和get方法的实现。
- MyArrayList提供基本的例程,比如:size、isEmpty和clear,并提供remove和add方法。
- MyArrayList将提供一个实现Iterator接口的类。
基本类
import java.util.Iterator;
public class MyArrayList<T> implements Iterable<T> {
private static final int DEFAULT_CAPACITY=10;
private int theSize;
private T[] theItems;
public MyArrayList(){
clear();
}
public void clear(){
theSize=0;
ensureCapacity(DEFAULT_CAPACITY);
}
public int size(){
return theSize;
}
public boolean isEmpty(){
return size()==0;
}
public void trimToSize(){
ensureCapacity(size());
}
public T get(int index){
if(index<0 || index>=size())
throw new ArrayIndexOutOfBoundsException();
return theItems[index];
}
public T set(int index,T newVal){
if(index<0 || index>=size())
throw new ArrayIndexOutOfBoundsException();
T old=theItems[index];
theItems[index]=newVal;
return old;
}
public void ensureCapacity(int newCapacity){
if(newCapacity<theSize)
return;
T[] old=theItems;
theItems=(T[])new Object[newCapacity];
for(int i=0;i<size();i++)
theItems[i]=old[i];
}
public boolean add(T x){
add(size(),x);
return true;
}
public void add(int index,T x){
if(theItems.length == size())
ensureCapacity(size()*2+1);
for(int i=theSize;i>index;i--)
theItems[i]=theItems[i-1];
theItems[index]=x;
theSize++;
}
public T remove(int index){
T removeItem=theItems[index];
for(int i=index;i<size()-1;i++)
theItems[i]=theItems[i+1];
theSize--;
return removeItem;
}
public java.util.Iterator<T> iterator(){
return new ArrayListIterator();
}
private class ArrayListIterator implements java.util.Iterator<T>{
private int current=0;
@Override
public boolean hasNext() {
return current<size();
}
@Override
public T next() {
if(!hasNext())
throw new java.util.NoSuchElementException();
return theItems[current++];
}
public void remove(){
MyArrayList.this.remove(--current);
}
}
}
迭代器、Java嵌套类和内部类
ArrayListIterator使用了一个复杂Java结构,叫做内部类。Java中的内部类有一些微妙的性质,为了了解内部类是如何工作的,接下来我们讲解一些选取内部类的思路。
思路1:使ArrayListIterator成为一个顶级类
//思路1:使ArrayListIterator成为一个顶级类
public class MyArrayList<T> implements Iterable<T> {
private static final int DEFAULT_CAPACITY=10;
private int theSize;
private T[] theItems;
...
public java.util.Iterator<T> iterator(){
return new ArrayListIterator<T>();
}
}
class ArrayListIterator<T> implements java.util.Iterator<T>{
private int current=0;
@Override
public boolean hasNext() {
return current<size();
}
@Override
public T next() {
if(!hasNext())
throw new java.util.NoSuchElementException();
return theItems[current++];
}
}
上图程序的问题在于,theItems[current++]
是非法的,因为theItems不是ArrayListIterator的一部分;它是MyArrayList的一部分,因此程序根本没有意义。
为了解决上述程序中迭代器中没有数组的问题,我们可以使用迭代器存储MyArrayList的引用来解决在迭代器中没有数组的问题。
public class MyArrayList<T> implements Iterable<T> {
private static final int DEFAULT_CAPACITY=10;
private int theSize;
private T[] theItems;
...
public java.util.Iterator<T> iterator(){
return new ArrayListIterator<T>();
}
}
class ArrayListIterator<T> implements java.util.Iterator<T>{
private int current=0;
private MyArrayList<T> theList;
...
public ArrayListIterator(MyArrayList<T> list){
thelist=list;
}
@Override
public boolean hasNext() {
return current<thelist.size();
}
@Override
public T next() {
if(!hasNext())
throw new java.util.NoSuchElementException();
return thelist.theItems[current++];
}
}
上述程序的问题在于theItems是MyArrayList中的私有域,所以访问它是非法的。虽然我们可以把private改为稍微宽松的可见性(比如:protected或public),但是这也违背了oop的基本原则,数据应该尽可能地隐蔽。
我们接着使用另一种方案,使ArrayListIterator成为一个嵌套类,使用关键字static来表示它是嵌套的,MyArrayList为其外部类。
public class MyArrayList<T> implements Iterable<T> {
private static final int DEFAULT_CAPACITY=10;
private int theSize;
private T[] theItems;
...
public java.util.Iterator<T> iterator(){
return new ArrayListIterator<T>(this);
}
class static ArrayListIterator<T> implements java.util.Iterator<T>{
private int current=0;
private MyArrayList<T> theList;
...
public ArrayListIterator(MyArrayList<T> list){
thelist=list;
}
@Override
public boolean hasNext() {
return current<thelist.size();
}
@Override
public T next() {
if(!hasNext())
throw new java.util.NoSuchElementException();
return thelist.theItems[current++];
}
}
}
而嵌套类的问题在于,在我们的原始设计中,当编写theItems而不引用其所在的MyArrayList的时候,是无效的,因为编译器不可能计算出哪个MyArrayList在被引用。要是我们自己不必明了这一点就好了,而这恰恰是内部类要求我们所做的。
当声明一个内部类的时候,编译器则添加对外部类对象的一个隐式引用,该对象引起内部类对象的构造。
public class MyArrayList<T> implements Iterable<T> {
private static final int DEFAULT_CAPACITY=10;
private int theSize;
private T[] theItems;
...
public java.util.Iterator<T> iterator(){
return new ArrayListIterator<T>(this);
}
private class ArrayListIterator implements java.util.Iterator<T>{
private int current=0;
@Override
public boolean hasNext() {
return current<size();
}
@Override
public T next() {
if(!hasNext())
throw new java.util.NoSuchElementException();
return theItems[current++];
}
public void remove(){
MyArrayList.this.remove(--current);
}
}
}
如果外部类的名字是Outer,则隐式引用就是Outer.this。由于迭代器中的remove方法与MyArrayList中的remove方法冲突,因此我们必须使用MyArrayList.this.remove。
LinkedList的实现
这里使用双链表来实现LinkedList,而且还需要保留到该表两端的引用。在设计LinkedList方面,我们将需要提供三个类:
- MyLinkedList类本身,它包括到两端的链、表的大小以及一些方法
- Node类,一个私有的嵌套类。一个节点包含数据以及到前一个节点、后一个节点的链,而且还有一些适当的构造方法。
- LinkedListIterator类,该类抽象了位置的概念,是一个私有类,并且实现了Iterator接口。
具有头节点和尾节点的双链表
具有头节点和尾节点的空链表
在链表中节点的删除以及插入操作中,如果在头节点前插入一个节点与在链表中间部分插入一个节点会有不同的操作。为了统一以及简化这些操作编码,我们在链表前端和后端都标记了额外的节点。代码如下:
package Chapter03;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
public class MyLinkedList<T> implements Iterable<T> {
private int theSize;
private int modCount=0;
private Node<T> beginMarker;
private Node<T> endMarker;
private static class Node<T>{
public Node(T data, Node<T> prev, Node<T> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
public T data;
public Node<T> prev;
public Node<T> next;
}
public MyLinkedList(){
clear();
}
public int Size(){
return theSize;
}
public boolean isEmpty(){
return Size()==0;
}
public void clear() {
beginMarker=new Node<>(null,null,null);
endMarker=new Node<>(null,null,null);
beginMarker.next=endMarker;
endMarker.prev=beginMarker;
theSize=0;
modCount++;
}
public boolean add(T x){
add(Size(),x);
return true;
}
public void add(int index,T x){
addBefore(getNode(index),x);
}
public T get(int index){
return (T) getNode(index).data;
}
public T set(int index,T newVal){
Node<T> p=getNode(index);
T oldVal=p.data;
p.data=newVal;
return oldVal;
}
public T remove(int index){
return (T) remove(getNode(index));
}
private Node getNode(int index) {
Node<T> p;
if(index<0 || index>Size()){
throw new IndexOutOfBoundsException();
}
if(index<=Size()/2){
p=beginMarker.next;
for (int i=0;i<index;i++){
p=p.next;
}
}else{
p=endMarker;
for (int i=Size();i>index;i--){
p=p.prev;
}
}
return p;
}
public Iterator<T> iterator(){
return new LinkedListIterator();
}
private class LinkedListIterator implements Iterator<T>{
private Node<T> current=beginMarker.next;
private int expectedModCount=modCount;
private boolean okToRemove=false;
public boolean hasNext(){
return current!=endMarker;
}
public T next(){
if(modCount!=expectedModCount)
throw new ConcurrentModificationException();
if(!hasNext())
throw new NoSuchElementException();
T nextItem=current.data;
current=current.next;
okToRemove=true;
return nextItem;
}
public void remove(){
if(modCount!=expectedModCount)
throw new ConcurrentModificationException();
if(!okToRemove)
throw new IllegalStateException();
MyLinkedList.this.remove(current.prev);
okToRemove=false;
expectedModCount++;
}
}
private void addBefore(Node node, T x) {
Node<T> newNode=new Node<T>(x,node.prev ,node);
newNode.prev.next=newNode;
node.prev=newNode;
theSize++;
modCount++;
}
private T remove(Node<T> node){
node.next.prev=node.prev;
node.prev.next=node.next;
theSize--;
modCount++;
return node.data;
}
}
为了检测迭代期间集合被修改的情况,迭代器在迭代器中维护了一个局部变量expectedModCount,当使用迭代器进行遍历链表的时候,如果在遍历中进行remove操作,而remove操作不是通过迭代器进行操作的时候,会报错。
双链表的插入操作,这里使用的是头插法
private void addBefore(Node node, T x) {
Node<T> newNode=new Node<T>(x,node.prev ,node);
newNode.prev.next=newNode;
node.prev=newNode;
theSize++;
modCount++;
}
链表中节点删除的代码:
private T remove(Node<T> node){
node.next.prev=node.prev;
node.prev.next=node.next;
theSize--;
modCount++;
return node.data;
}