现在是2020年10月3日16:41:45,继续对Java的集合机制进行介绍。再罗列一下,我们要讨论集合的三个方面:
- 集合简介与集合接口相关的常用方法
- 集合接口常用实现类的数据结构及其存取元素过程
- 集合元素的迭代遍历与迭代器模式介绍
本次分享对集合元素的遍历以及简单地以ArrayList为例介绍一下迭代器模式。
三、集合元素的迭代遍历与迭代器模式介绍[续]
1、集合元素的遍历方式
(1)Collection集合的迭代方式
package cn.drgn.javase.api.collection_map.iterate;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** Create by Drgn on 2020/10/1 16:57*/
public class CollectionIterateTest {
public static void main(String[] args) {
Collection<String> names = new ArrayList<>();names.add("tom");names.add("nana");names.add("jack");names.add("tim");// 遍历方式1:使用迭代器Iterator<String> iterator = names.iterator();while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");}System.out.println("\n------------------------------");// 遍历方式2:使用foreach// foreach机制:for(类型 局部变量名:容器引用){ // 逻辑处理 }for (String name : names) {
System.out.print(name + " ");}}
}
运行结果:
(2)Map集合的迭代方式
package cn.drgn.javase.api.collection_map.iterate;import java.util.*;/*** Create by Drgn on 2020/10/1 17:06*/
public class MapIterateTest {
public static void main(String[] args) {
Map<Integer, String> map = new TreeMap<>();map.put(4, "cat");map.put(2, "dog");map.put(1, "lion");map.put(3, "pig");// 遍历方式1:先获取键的集合,再获取值Set<Integer> keySet = map.keySet();for (Integer key : keySet) {
System.out.println(key + " ---> " + map.get(key));}System.out.println("-----------------------");// 遍历方式2:先获取键值对的Entry, 然后获取键值对Set<Map.Entry<Integer, String>> entrySet = map.entrySet();for (Map.Entry<Integer, String> entry : entrySet) {
System.out.println(entry.getKey() + " ---> " + entry.getValue());}System.out.println("-----------------------");// 遍历方式3:只能获取值的集合Collection<String> values = map.values();for (String val : values) {
System.out.print(val + " ");}}
}
运行结果:
二、迭代器模式介绍
- 迭代器模式是一种设计模式,它用于实现聚合对象对其元素的遍历访问。所谓聚合对象,指的是能够容纳多个元素的一种类型,比如数组、集合等。他们的一种类型的或者多种类型的多对象存储容器,可以通过遍历对容器中的所有元素进行统一逻辑处理。
- 迭代器模式的实现,可以让使用者不再关注遍历具体的实现细节,只需通过调用接口获取某一聚合对象的迭代器,就可以对该聚合对象进行元素的遍历访问。
- 接下来以ArrayList类实现的迭代器为例,从源码的角度了解一下迭代器模式。
(1)先看迭代器接口Iterator,它定义了作为迭代器需要的一些操作方法,如hasNext()、next()等。
// Iterator迭代器接口
public interface Iterator<E> {
boolean hasNext();E next();default void remove() {
throw new UnsupportedOperationException("remove");}default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);while (hasNext())action.accept(next());}
}
(2)再看ArrayList集合类,在该类中定义了一个私有的成员内部类Itr,它实现了Iterator接口中的抽象方法,是Iterator接口在ArrayList类中的具体实现,也就是ArrayList的一个迭代器。ArrayList类通过iterator()方法返回这个Itr迭代器对象,接着使用者即可通过这个迭代器调用Iterator接口方法(多态)来遍历ArrayList容器中存储的元素。
// ArrayList
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
transient Object[] elementData;// List's methods...// 实现Iterable接口(Collection)声明的iterator方法,获取迭代器public Iterator<E> iterator() {
return new Itr();}// 私有化的成员内部类,实现迭代器Iterator接口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];}final void checkForComodification() {
// 检查集合结构是否更新if (modCount != expectedModCount)throw new ConcurrentModificationException();}// 从底层集合中删除此迭代器返回的最后一个元素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) {
// 实现过程,忽略...}}
}
我们可以先画出ArrayList集合中实现的迭代器模式,如下所示。
首先,迭代器涉及到的四个基本角色:抽象聚合、抽象迭代、具体聚合、具体迭代。
(1)抽象聚合中定义了具体聚合类的抽象方法,具体聚合类必须根据自身设计的数据结构来实现抽象聚合类中的方法,如ArrayList、LinkedList的数据结构分别是数组和双向链表,他们都实现了List这个抽象聚合接口,并且根据他们自身的数据结构的不同,实现接口操作的细节也不相同。聚合的理解前面已经讲过,不再赘述。
(2)抽象迭代中定义了具体迭代器的抽象方法,这些方法是迭代过程中迭代操作的集合,如hasNext(),next()等,具体迭代器对抽象迭代器中的抽象方法加以具体实现。
(3)注意:具体迭代器是某一具体聚合类的迭代器,离开了具体聚合类,具体迭代器将无法生存。具体迭代器中的迭代操作必须根据具体聚合类的数据结构进行具体实现。但是具体聚合类可以重新依赖新的抽象迭代接口实现类,替换掉原来依赖的具体迭代器。这就是迭代器模式带来的可拔插、可扩展的好处。
(4)另外,根据不同的业务需要,一个具体聚合类可能有多个具体迭代器,而一个具体迭代器只能服务于一个具体聚合类。
好的,关于集合元素的迭代和根据集合底层实现的扩展引出的迭代器模式就介绍到这里。
至此,Java集合机制的分享到此告一段落。因为其中尽量避免专有名词的出现,使用大白话进行分享,可能篇幅较长,敬请原谅。另外,在分享过程中难免可能有一些字可能是错别字,或者有一些错误,请发现了的朋友帮我指正,谢谢阅读!