L'itération n'est certainement pas étrangère à ceux d'entre nous qui travaillent en Java. Nous utilisons souvent l'interface d'itération fournie par JDK pour itérer les collections Java.
Iterator iterator = list.iterator(); while(iterator.hasNext()){ String string = iterator.next(); //do something }
En fait, on peut simplement comprendre l'itération car Traversal est une classe de méthodes standardisée pour parcourir tous les objets dans divers conteneurs. Il s'agit d'un modèle de conception très typique. Le modèle d’itérateur est la méthode d’accès standard pour parcourir les classes de collection. Il peut abstraire la logique d'accès de différents types de classes de collection, évitant ainsi d'exposer la structure interne de la collection au client. C'est ainsi que nous procédons lorsqu'il n'y a pas d'itérateurs. Comme suit :
Pour les tableaux, nous utilisons des indices pour traiter :
int[] arrays = new int[10]; for(int i = 0 ; i < arrays.length ; i++){ int a = arrays[i]; //do something }
Voici comment ArrayList est géré :
List<String> list = new ArrayList<String>(); for(int i = 0 ; i < list.size() ; i++){ String string = list.get(i); //do something }
Il n'existe aucun moyen de séparer la logique d'accès des classes de collection et du code client. En même temps, chaque collection correspond à une méthode de parcours et le code client ne peut pas être réutilisé. Dans les applications pratiques, il est assez difficile d’intégrer les deux ensembles ci-dessus. Ainsi, afin de résoudre les problèmes ci-dessus, le mode Itérateur est né. Il utilise toujours la même logique pour parcourir la collection. Cela élimine le besoin pour le client lui-même de maintenir la structure interne de la collection, et tous les états internes sont gérés par Iterator. Le client ne traite jamais directement avec la classe de collection. Il contrôle toujours l'Iterator et lui envoie les commandes "forward", "backward" et "get current element" pour parcourir indirectement toute la collection.
Ce qui précède n'est qu'une brève explication du mode Iterator. Jetons un coup d'œil à l'interface Iterator en Java pour voir comment elle est implémentée.
En Java, Iterator est une interface qui fournit uniquement l'itération. les règles de base sont définies dans le JDK comme suit : un itérateur qui parcourt la collection. Les itérateurs remplacent l'énumération dans Java Collections Framework. Il existe deux différences entre les itérateurs et les énumérations :
Supprimer l'élément.
2. Le nom de la méthode a été amélioré.
L'interface est définie comme suit :
public interface Iterator { boolean hasNext(); Object next(); void remove(); }
Où :
Object next() : renvoie une référence à l'élément qui vient d'être traversé par l'itérateur, la valeur de retour est Object, qui doit être convertie dans le type dont vous avez besoin
boolean hasNext() : Déterminez s'il existe des éléments accessibles dans le conteneur
void remove() : Supprime l'élément qui vient de traverser l'itérateur
Pour nous, nous n'avons généralement besoin d'utiliser que les méthodes next() et hasNext() pour terminer l'itération. Comme suit :
for(Iterator it = c.iterator(); it.hasNext(); ) { Object o = it.next(); //do something }
前面阐述了Iterator有一个很大的优点,就是我们不必知道集合的内部结果,集合的内部结构、状态由Iterator来维持,通过统一的方法hasNext()、next()来判断、获取下一个元素,至于具体的内部实现我们就不用关心了。但是作为一个合格的程序员我们非常有必要来弄清楚Iterator的实现。下面就ArrayList的源码进行分析分析。
下面就ArrayList的Iterator实现来分析,其实如果我们理解了ArrayList、Hashset、TreeSet的数据结构,内部实现,对于他们是如何实现Iterator也会胸有成竹的。因为ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引即可,其方法的实现比较简单。
在ArrayList内部首先是定义一个内部类Itr,该内部类实现Iterator接口,如下:
private class Itr implements Iterator<E> { //do something }
而ArrayList的iterator()方法实现:
public Iterator<E> iterator() { return new Itr(); }
所以通过使用ArrayList.iterator()方法返回的是Itr()内部类,所以现在我们需要关心的就是Itr()内部类的实现:
在Itr内部定义了三个int型的变量:cursor、lastRet、expectedModCount。其中cursor表示下一个元素的索引位置,lastRet表示上一个元素的索引位置
int cursor; int lastRet = -1; int expectedModCount = modCount;
从cursor、lastRet定义可以看出,lastRet一直比cursor少一所以hasNext()实现方法异常简单,只需要判断cursor和lastRet是否相等即可。
public boolean hasNext() { return cursor != size; }
对于next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可,
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; //cursor + 1 return (E) elementData[lastRet = i]; //lastRet + 1 且返回cursor处元素 }
checkForComodification()主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过。在java提高篇(二一)-----ArrayList中已经阐述了。modCount用于记录ArrayList集合的修改次数,初始化为0,,每当集合被修改一次(结构上面的修改,内部update不算),如add、remove等方法,modCount + 1,所以如果modCount不变,则表示集合内容没有被修改。该机制主要是用于实现ArrayList集合的快速失败机制,在Java的集合中,较大一部分集合是存在快速失败机制的,这里就不多说,后面会讲到。所以要保证在遍历过程中不出错误,我们就应该保证在遍历过程中不会对集合产生结构上的修改(当然remove方法除外),出现了异常错误,我们就应该认真检查程序是否出错而不是catch后不做处理。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
对于remove()方法的是实现,它是调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可。
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(); } }
这里就对ArrayList的Iterator实现讲解到这里,对于Hashset、TreeSet等集合的Iterator实现,各位如果感兴趣可以继续研究,个人认为在研究这些集合的源码之前,有必要对该集合的数据结构有清晰的认识,这样会达到事半功倍的效果!!!!
————————————————————————————— ———————— ————————————
Ce qui précède est l'article d'amélioration de Java (Partie 3 10) -- ---Le contenu d'Iterator, veuillez faire attention au site Web PHP chinois (m.sbmmt.com) pour plus de contenu connexe !