之前一直搞不清楚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方法的第二个原因。