迭代器模式
迭代器模式定义:提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
相信java程序员对迭代器模式都不陌生,我们集合类就是采用的这种模式,但是为什么要用呢?下面我先自己写一版demo版的Arraylist、linkedList和hashmap。
package iterator; public class MyArrayList<E> { private final int INCREMENT = 10; @SuppressWarnings("unchecked") private Object[] array = new Object[10]; private int size; @SuppressWarnings("unchecked") public void add(E e) { if(size<array.length) { array[size++] = e; }else { E[] copy = (E[]) new Object[array.length+ INCREMENT]; System.arraycopy(array, 0, copy, 0, size); copy[size++] = e; array= copy; } } @SuppressWarnings("unchecked") public Object[] toArray() { Object[] copy = new Object[size]; System.arraycopy(array, 0, copy, 0, size); return copy; } @SuppressWarnings("unchecked") public E get(int index) { return (E) array[index]; } }
package iterator; public class MyLinkedList<E> { private Entry<E> header = new Entry<E>(null, null, null); private Entry<E> last ; private int size; public MyLinkedList() { header.next =header.previous=last=header; } public void add(E e) { Entry<E> entry = new Entry<E>(e, last, null); entry.previous.next = entry; last = entry; size++; } private static class Entry<E>{ E value; Entry<E> previous; Entry<E> next; public Entry(E value,Entry<E> previous, Entry<E> next) { super(); this.value = value; this.previous = previous; this.next = next; } } public Object[] toArray(){ Object[] result = new Object[size]; int i = 0; for (Entry<E> e = header.next; e != null; e = e.next) result[i++] = e.value; return result; } }
package iterator; public class MyHashMap { private int size; private MyEntry[] table; public MyHashMap() { super(); this.size = 0; this.table = new MyEntry[10]; } private int index(Object key) { return key.hashCode()% table.length; } public int size() { return this.size; } public void put (Object key,Object value) { if(key ==null) return; int index = index(key); MyEntry entry = table[index]; while(entry != null) { if(entry.getKey().hashCode()== key.hashCode() &&(entry.getKey()== key || entry.getKey().equals(key))) { entry.setValue(value); return; } entry = entry.getNext(); } add(index, key, value); size ++; } private void add(int index,Object key,Object value) { MyEntry newEntry = new MyEntry(key, value, table[index]); table[index] = newEntry; } public Object get(Object key) { if(key==null) return null; MyEntry entry = getEntry(key); return entry==null?null:entry.getValue(); } public MyEntry getEntry(Object key) { int index = index(key); MyEntry entry = table[index]; while(entry != null) { if(entry.getKey().hashCode()== key.hashCode() &&(entry.getKey()== key || entry.getKey().equals(key))) { return entry; } entry =entry.getNext(); } return null; } static class MyEntry{ private final Object key; private Object value; private MyEntry next; public MyEntry(Object key, Object value, MyEntry next) { super(); this.key = key; this.value = value; this.next = next; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public MyEntry getNext() { return next; } public void setNext(MyEntry next) { this.next = next; } public Object getKey() { return key; } } public Object[] toArray(){ MyArrayList<MyEntry> result = new MyArrayList<MyEntry>(); for (int i = 0; i < table.length; ++i) { for (MyEntry e = table[i]; e != null; e = e.next) result.add(e); } return result.toArray(); } }
在代码中,我都是通过一个toArray方法将集合中的元素存放到数组中,然后使用时通过这个方法获取数组,然后按数组的方式去遍历,当然,为了迎合面向接口的思想,你可以添加一个接口规定toArray的行为,让三个类去实现它。但是在这里有一个很大的弊端,那就是在使用数组遍历集合类的时候,其实遍历了两次。 在这三个类中,由于System的arraycopy和set的toArray方法是黑箱子,所以最明显的便是LinkedList的实现,它是先遍历了一遍链表,做出来一个数组,然后当客户端获得到这个数组的时候,则需要再来一次循环,去遍历每一个元素。当然针对arraylist,我们可以通过数组自带的index来遍历,同时还解决了二次遍历的问题,但是其它两个就不能这样做。
三个集合类,如果统一提供数组给客户端遍历,那么在遍历过程中会出现重复遍历的现象。而如果消除这种重复遍历,则由于内部数据结构的不同,三个集合类无法做到像提供数组一样,给客户端提供统一的遍历方式。为了解决上面的问题,迭代器模式就随之出现了。我们先来看看迭代器模式在百度百科中的类图。
看着上面的类图,我们可以分析出来,上面我们所写的ArrayList等三个类都属于ConcreteAggregate的位置,如果给toarray抽象出接口,那么就相当于Aggregate,我们所少的就是Iterator接口和具体的迭代器。这里就不再自己写了,直接用JDK的。
Iterator源码,所有的迭代器实现这个接口
package java.util; import java.util.function.Consumer; 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()); } }
Iterable源码,集合类实现这个接口,表名自己可迭代
package java.lang; import java.util.Iterator; import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; public interface Iterable<T> { 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); } }
改造后的集合类
package iterator; import java.util.Iterator; public class MyArrayList<E> implements Iterable<E>{ private final int INCREMENT = 10; private Object[] array = new Object[10]; private int size; @SuppressWarnings("unchecked") public void add(E e) { if(size<array.length) { array[size++] = e; }else { E[] copy = (E[]) new Object[array.length+ INCREMENT]; System.arraycopy(array, 0, copy, 0, size); copy[size++] = e; array= copy; } } public Object[] toArray() { Object[] copy = new Object[size]; System.arraycopy(array, 0, copy, 0, size); return copy; } @SuppressWarnings("unchecked") public E get(int index) { return (E) array[index]; } @Override public Iterator<E> iterator() { return new ArrayIterator(); } private class ArrayIterator implements Iterator<E>{ int cursor; @Override public boolean hasNext() { return cursor<size; } @SuppressWarnings("unchecked") @Override public E next() { int i = cursor; Object[] elementData = MyArrayList.this.array; cursor = i + 1; return (E) elementData[i]; } } }
package iterator; import java.util.Iterator; public class MyLinkedList<E> implements Iterable<E>{ private Entry<E> header = new Entry<E>(null, null, null); private Entry<E> last ; private int size; public MyLinkedList() { header.next =header.previous=last=header; } public void add(E e) { Entry<E> entry = new Entry<E>(e, last, null); entry.previous.next = entry; last = entry; size++; } private static class Entry<E>{ E value; Entry<E> previous; Entry<E> next; public Entry(E value,Entry<E> previous, Entry<E> next) { super(); this.value = value; this.previous = previous; this.next = next; } } public Object[] toArray(){ Object[] result = new Object[size]; int i = 0; for (Entry<E> e = header.next; e != null; e = e.next) result[i++] = e.value; return result; } @Override public Iterator<E> iterator() { return new LinkIterator(); } private class LinkIterator implements Iterator<E>{ private Entry<E> lastReturned; private Entry<E> next; private int nextIndex; LinkIterator(){ next = header.next; } @Override public boolean hasNext() { return nextIndex < size; } @Override public E next() { lastReturned = next; nextIndex++; next= next.next; return lastReturned.value; } } }
package iterator; import java.util.Iterator; import java.util.NoSuchElementException; public class MyHashMap<E> implements Iterable<E>{ private int size; private MyEntry[] table; public MyHashMap() { super(); this.size = 0; this.table = new MyEntry[10]; } private int index(Object key) { return key.hashCode()% table.length; } public int size() { return this.size; } public void put (Object key,Object value) { if(key ==null) return; int index = index(key); MyEntry entry = table[index]; while(entry != null) { if(entry.getKey().hashCode()== key.hashCode() &&(entry.getKey()== key || entry.getKey().equals(key))) { entry.setValue(value); return; } entry = entry.getNext(); } add(index, key, value); size ++; } private void add(int index,Object key,Object value) { MyEntry newEntry = new MyEntry(key, value, table[index]); table[index] = newEntry; } public Object get(Object key) { if(key==null) return null; MyEntry entry = getEntry(key); return entry==null?null:entry.getValue(); } public MyEntry getEntry(Object key) { int index = index(key); MyEntry entry = table[index]; while(entry != null) { if(entry.getKey().hashCode()== key.hashCode() &&(entry.getKey()== key || entry.getKey().equals(key))) { return entry; } entry =entry.getNext(); } return null; } public static class MyEntry{ private final Object key; private Object value; private MyEntry next; public MyEntry(Object key, Object value, MyEntry next) { super(); this.key = key; this.value = value; this.next = next; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public MyEntry getNext() { return next; } public void setNext(MyEntry next) { this.next = next; } public Object getKey() { return key; } } public Object[] toArray(){ MyArrayList<MyEntry> result = new MyArrayList<MyEntry>(); for (int i = 0; i < table.length; ++i) { for (MyEntry e = table[i]; e != null; e = e.next) result.add(e); } return result.toArray(); } @Override public Iterator<E> iterator() { return new HashIterator(); } private class HashIterator implements Iterator<E>{ MyEntry next; // next entry to return @SuppressWarnings("unused") MyEntry current; // current entry int index; // current slot HashIterator() { MyEntry[] t = table; current = next = null; index = 0; if (t != null && size > 0) { // advance to first entry do {} while (index < t.length && (next = t[index++]) == null); } } public final boolean hasNext() { return next != null; } @SuppressWarnings("unchecked") @Override public E next() { MyEntry[] t; MyEntry e = next; if (e == null) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null) { do {} while (index < t.length && (next = t[index++]) == null); } return (E) e; } } }
改造后的集合类遍历方式
package iterator; import java.util.Iterator; public class Client { public static void main(String[] args) { MyArrayList<String> mar = new MyArrayList<String>(); mar.add("a"); mar.add("b"); mar.add("c"); mar.add("d"); Iterator<String> it = mar.iterator(); while(it.hasNext()) { String e = it.next(); System.out.println(e); } // MyLinkedList<String> myl = new MyLinkedList<>(); // myl.add("a"); // myl.add("b"); // myl.add("c"); // myl.add("d"); // Iterator<String> it = myl.iterator(); // while(it.hasNext()) { // String e = it.next(); // System.out.println(e); // } // MyHashMap<MyEntry> map = new MyHashMap<MyEntry>(); // map.put("asd", "asd"); // map.put("zxc", "zx"); // map.put("qwe", "qwe"); // map.put("bnm", "bnm"); // Iterator<MyEntry> it = map.iterator(); // while(it.hasNext()) { // MyEntry e = it.next(); // System.out.println(e.getKey() +"-----"+e.getValue()); // } } }
通过以上例子,可以看到迭代器模式解决了哪些问题:
1、迭代器模式可以提供统一的迭代方式,这个要归功于Iterator接口。
2、迭代器模式可以在对客户透明的前提下,做出各种不同的迭代方式。
3、在迭代的时候不需要暴露聚合对象的内部表示,我们只需要认识Iterator即可。
4、解决了基于数组的迭代方式中重复遍历的问题。
当然JDK中Hashmap并没有实现Iterable接口,并且官方也不推荐使用迭代器去遍历,但是底层的原理是一致的,有兴趣的同学可以自己去看下源码。