君临-行者无界

导航

迭代器模式

  迭代器模式定义:提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。

  相信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接口,并且官方也不推荐使用迭代器去遍历,但是底层的原理是一致的,有兴趣的同学可以自己去看下源码。

 

  

posted on 2019-01-14 12:10  请叫我西毒  阅读(183)  评论(0编辑  收藏  举报