Java迭代器原理
1迭代器模式
迭代器是一種設(shè)計(jì)模式,這種模式用于順序訪問(wèn)集合對(duì)象的元素,不需要知道集合對(duì)象的底層表示。
一般實(shí)現(xiàn)方式如下:(來(lái)自)
?
public interface Iterator {public boolean hasNext();public Object next(); } public interface Container {public Iterator getIterator(); } public class NameRepository implements Container {public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};@Overridepublic Iterator getIterator() {return new NameIterator();}private class NameIterator implements Iterator {int index;@Overridepublic boolean hasNext() {if(index < names.length){return true;}return false;}@Overridepublic Object next() {if(this.hasNext()){return names[index++];}return null;} } } public class IteratorPatternDemo {public static void main(String[] args) {NameRepository namesRepository = new NameRepository();for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){String name = (String)iter.next();System.out.println("Name : " + name);} } }一般情況,我們自己開(kāi)發(fā)時(shí)很少自定義迭代器,因?yàn)閖ava本身已經(jīng)把迭代器做到內(nèi)部中了
2Java中的迭代器
(1)Iterator接口
package java.util;import java.util.function.Consumer;public interface Iterator<E> {/*** 如果迭代器又更多的元素,返回true。* 換句話說(shuō),如果next方法返回一個(gè)元素而不是拋出一個(gè)異常,則返回true。
*/ boolean hasNext();//返回迭代器中的下一個(gè)元素,如果沒(méi)有,則拋出NoSuchElementException異常 E next();/**
* 從底層集合中刪除該迭代器返回的最后一個(gè)元素(可選操作)。每執(zhí)行一次next方法這個(gè)方法只能被調(diào)用1次。* 如果在迭代過(guò)程中,除了調(diào)用此方法之外,任何其他方法修改基礎(chǔ)集合,則迭代器的行為是不確定的。 * 默認(rèn)實(shí)現(xiàn)是拋出一個(gè)UnsupportedOperationException異常,不執(zhí)行其他操作。 * 如果每次調(diào)用該方法前next方法沒(méi)有執(zhí)行,則拋出IllegalStateException異常。
*/default void remove() {throw new UnsupportedOperationException("remove");}/**
* @since 1.8(函數(shù)編程)。 * 對(duì)每個(gè)剩余元素執(zhí)行給定的操作,直到所有元素都被處理或動(dòng)作拋出異常為止。 * 如果指定了該順序,則按迭代順序執(zhí)行操作。動(dòng)作拋出的異常被傳遞給調(diào)用者。 * 如果action為null,則拋出NullPointerException
*/default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());} }
?
首先,Iterator接口是屬于java.util包的。然后里面只有4個(gè)方法(用法介紹請(qǐng)看注釋)。
forEachRemaining方法是Java8函數(shù)編程新加入的。作用是對(duì)前游標(biāo)之后的每個(gè)元素進(jìn)行處理(沒(méi)有返回值),具體怎么處理根據(jù)傳入的函數(shù)。這項(xiàng)里操作使得迭代器更加靈活,操作粒度更加細(xì)致。
補(bǔ)充:default關(guān)鍵字可以讓接口中的方法可以有默認(rèn)的函數(shù)體,當(dāng)一個(gè)類實(shí)現(xiàn)這個(gè)接口時(shí),可以不用去實(shí)現(xiàn)這個(gè)方法,當(dāng)然,這個(gè)類若實(shí)現(xiàn)這個(gè)方法,就等于子類覆蓋了這個(gè)方法,最終運(yùn)行結(jié)果符合Java多態(tài)特性。(Java8的新特性)
類注釋:
/*** An iterator over a collection. {@code Iterator} takes the place of {@link Enumeration} in the Java Collections Framework. Iterators* differ from enumerations in two ways:** <ul>* <li> Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.* <li> Method names have been improved.* </ul>** <p>This interface is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html">Java Collections Framework</a>.*/Iterator是集合上的迭代器。在Java集合框架中Iterator用來(lái)替代Enumeration,Iterator與Enumeration有以下兩點(diǎn)區(qū)別:
- Iterator允許調(diào)用者通過(guò)定于語(yǔ)義良好的迭代器刪除底層集合中的元素。
- 方法名稱已得到改進(jìn)。
這個(gè)接口是Java集合框架的成員。除了如上兩點(diǎn)不同外,Java8版本Iterator還加入了函數(shù)式編程。
(2)Iterable接口
package java.lang;// 實(shí)現(xiàn)此接口,允許對(duì)象成為“for-each loop”語(yǔ)句的目標(biāo)。 public interface Iterable<T> {// 返回類型為 T元素的迭代器。Iterator<T> iterator();/*** @since 1.8
* 對(duì)Iterable的每個(gè)元素執(zhí)行給定的操作,直到所有元素都被處理或動(dòng)作引發(fā)異常。
* 除非實(shí)現(xiàn)類另有規(guī)定,否則按照迭代的順序執(zhí)行操作(如果指定了迭代順序)。 動(dòng)作拋出的異常被轉(zhuǎn)發(fā)給調(diào)用者。
* 拋出NullPointerException - 如果指定的動(dòng)作為空*/default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}}/*** @since 1.8
* 在Iterable描述的元素上創(chuàng)建一個(gè)Spliterator。Spliterator繼承了迭代器的fail-fast屬性。
* */default Spliterator<T> spliterator() {return Spliterators.spliteratorUnknownSize(iterator(), 0);} }
Java集合包最常用的有Collection和Map兩個(gè)接口的實(shí)現(xiàn)類。Map的實(shí)現(xiàn)類迭代器是內(nèi)部實(shí)現(xiàn)的,而Collection繼承了Iterable接口。
這里以ArrayList為例,梳理一下Iterator的工作流程。
ArrayList是Collection的子類,而Collection又實(shí)現(xiàn)了Iterable接口,Iterable接口里面有iterator()方法(該方法返回一個(gè)迭代器對(duì)象)。所以,ArrayList(或其父類)也必須實(shí)現(xiàn)iterator()方法。
iterator()方法返回一個(gè)Iterator對(duì)象,而前文中Iterator是個(gè)接口。我們不能不知道具體用哪個(gè)實(shí)現(xiàn)類也不能直接new接口,所以我們找到其直接父類AbstractList,查看iterator()到底如何實(shí)現(xiàn)的:
?
public Iterator<E> iterator() {return new Itr();}private class Itr implements Iterator<E> {// Index of element to be returned by subsequent call to next.int cursor = 0;/*** Index of element returned by most recent call to next or* previous. Reset to -1 if this element is deleted by a call* to remove.*/int lastRet = -1;/*** The modCount value that the iterator believes that the backing* List should have. If this expectation is violated, the iterator* has detected concurrent modification.*/int expectedModCount = modCount;public boolean hasNext() {return cursor != size();}public E next() {checkForComodification();try {int i = cursor;E next = get(i);lastRet = i;cursor = i + 1;return next;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}public void remove() {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {AbstractList.this.remove(lastRet);if (lastRet < cursor)cursor--;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException e) {throw new ConcurrentModificationException();}}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}?
這里是通過(guò)內(nèi)部類實(shí)現(xiàn)了Iterator接口,然后再將其實(shí)例對(duì)象返回。
?
原理很簡(jiǎn)單,每調(diào)用一次next方法,先返回當(dāng)前游標(biāo)指向位置的值,然后游標(biāo)往下移動(dòng)一位,直到游標(biāo)數(shù)值等于list的size。
而在ArrayList類里面又提供了一個(gè)實(shí)現(xiàn)版本:
/*** An optimized version of AbstractList.Itr*/private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;Itr() {}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();}}@Override@SuppressWarnings("unchecked")public void forEachRemaining(Consumer<? super E> consumer) {Objects.requireNonNull(consumer);final int size = ArrayList.this.size;int i = cursor;if (i >= size) {return;}final Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length) {throw new ConcurrentModificationException();}while (i != size && modCount == expectedModCount) {consumer.accept((E) elementData[i++]);}// update once at end of iteration to reduce heap write trafficcursor = i;lastRet = i - 1;checkForComodification();}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}next方法很好理解,和父類大概是一個(gè)意思。remove方法是調(diào)用ArrayList.this.remove(lastRet)實(shí)現(xiàn):
?
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 workreturn oldValue;}?
numMoved 是計(jì)算要移動(dòng)的元素個(gè)數(shù)(刪除數(shù)組的某一位置的值,后面的值要依次往前移)。
cursor = lastRet;
lastRet = -1;
表示刪除之后,游標(biāo)前移1位。為什么這么做?舉個(gè)例子,數(shù)組a = [1,2,3,4],若cursor = 2,游標(biāo)指向數(shù)字3,則lastRet = 1。當(dāng)刪除a[1]的時(shí)候,a = [1,3,4]。3對(duì)應(yīng)的位置變?yōu)?了,所以會(huì)有cursor = lastRet。
lastRet的值置為-1,這里很好的解釋了前面注釋中為什么remove方法一定要在next方法之后執(zhí)行了。
?
轉(zhuǎn)載于:https://www.cnblogs.com/ouym/p/8857448.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: 如何根据动态SQL代码自动生成DTO
- 下一篇: 编程使用资源文件实现多语言页面(In A