之前一直搞不清楚Iterator和Iterable到底有什么關系,它們的區別是什么,今天查看Java集合類源碼才發現其中的名堂,接下來給大家講講我的分析與理解。
Iterable接口
Iterable是接口,Iterable是1.5引入的新特性,Iterator是1.2就有了,二者都是為了迭代造作,Iterable只是包裝了Iterator,從而允許實現此接口的對象成為foreach語句的目標,而且這樣的話,更方便以后的擴展。
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
Java集合Collection接口就擴展了Iterable接口。實現Iterable接口的那些類就可以擁有增強的for循環,該循環施于這些類之上以觀察他們所有的項。
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
實現Iterable接口的集合必須提供一個稱為itrator的方法,該方法又返回一個Iterator類型的對象。所以例如List集合類,Set類,他們實現了Collection接口,自然實現了Iterable接口,在其源碼中有具體的iterator方法,例如:ArraryList類中,它實現了List接口
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
...
Iterator迭代器
Iterator接口的思路是,通過iterator方法,每個集合均可創建并返回給客戶一個實現Iterator接口的對象。
public interface Iterator<E> {
boolean hasNext();
T next();
void remove();
}
Iterator一般用于簡單遍歷集合中的元素。而Iterator還有一個有用的方法叫做remove()方法,相對于Collection中的remove而言,它具有更多的優點。
對比Collection中remove方法和Iterator中remove方法
- 抽象類AbstractCollection(實現了Collection接口)的remove方法
public boolean remove(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext()) {
if (it.next()==null) {
it.remove();
return true;
}
}
} else {
while (it.hasNext()) {
//找出要被刪除的項
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
Collection的remove方法必須首先找出要被刪除的項,開銷較大。
- Iterator中的remove方法
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);//remove()方法下面有給出
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
以上是ArrayList中其實現Iterator接口的內部類中remove方法,可能其中有些變量你看不懂,需要聯系整個源碼才能夠明白它大體的意思。
以下是ArrayList重寫AbstractList中remove()方法。
public E remove(int index) {
rangeCheck(index);//檢查是否越界
modCount++;
E oldValue = elementData(index);//得到要刪除的元素
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//復制數組
elementData[--size] = null; // clear to let GC do its work
return oldValue;//返回要刪除的元素
}
不難看出Iterator的remove方法并沒有一個一個比較找出所要刪除的項,而是知道要刪除項的準確位置,那么刪除它的開銷就小很多。
使用Iterator的基本法則
如果對正在被迭代的集合進行結構上的改變(即對集合使用add,remove或clear方法),那么迭代器就不再合法(并且在其后使用該迭代器時將會有ConcurrentModificationException異常被拋出)。然而,如果迭代器調用了它自己的remove方法,那這個迭代器仍然是合法的,所以這是我們有時候更愿意使用迭代器的remove方法的第二個原因。