Java设计模式(七)之迭代器模式(Iterator)

目录

自定义ArrayList

自定义LinkedList

自定义Collection

自定义Iterator和Iteratable


        Iterator模式在Java的集合框架中有使用。本文尽量使用源码解释Iterator模式。

自定义ArrayList

        我们先自定义MyArrayList类,如下:

package main.design_pattern.iterator;

import java.util.Arrays;

/**
 * Created by leboop on 2020/5/24.
 */
public class MyArrayList<T> {
    //    数组列表大小
    private int size;
    //    数组列表结构,数据列表真正存储的数据
    private Object[] elementData;
    //    空的数据列表
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //    数组列表的容量
    private static final int DEFAULT_CAPACITY = 10;
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public MyArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    //    构造方法
    public MyArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: " +
                    initialCapacity);
        }
    }

    /**
     * 数组列表扩容,最后的容量如下确定:
     * (1)如果实际容量没有超过容器容量,不扩容;
     * (2)如果实际容量已经超过容器容量,进行扩容
     *      扩容方法:总容量=原来容量二进制表示后右移1位表示的容量+原来容器容量
     *      如果扩容后容量不够,则直接使用实际容量作为容器容量
     *      如果扩容后容量巨大,超过允许的最大容量,容器容量直接设置为最大容量
     * (3)将原容器的数据拷贝到扩容后的容器中
     * @param minCapacity 实际容量
     */
    private void ensureCapacity(int minCapacity) {
//        如果数据列表是空的,实际容量设置为默认容量10和最小容量的最大值,比如最小容量=5,实际容量为10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 实际容量minCapacity已经超过容器容量,则扩容
        if (minCapacity - elementData.length > 0) {
            int oldCapacity = elementData.length;
//            扩容,比如现在容量是10,二进制为1010,扩容101=5,变为15
            int newCapacity = oldCapacity + (oldCapacity >> 1);
//            扩容后容量比实际容量还小,实际容量设置为扩容后的容量,也就是不使用扩容容量
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
//            扩容容量超过最大限制
            if (newCapacity - MAX_ARRAY_SIZE > 0) {
                if (minCapacity < 0) // overflow
                    throw new OutOfMemoryError();
                newCapacity = (minCapacity > MAX_ARRAY_SIZE) ?
                        Integer.MAX_VALUE :
                        MAX_ARRAY_SIZE;
            }
            // 拷贝
            elementData = Arrays.copyOf(elementData, newCapacity);
        }

    }

    /**
     * 添加一个元素:首先检查容量,如果容量不够,则进行扩容,扩容后将新元素添加进去
     *
     * @param e 新增元素
     * @return
     */
    public boolean add(T e) {
        // 容量保证
        ensureCapacity(size + 1);

        this.elementData[size++] = e;

        return true;
    }

    public T get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);

        return (T) elementData[index];
    }

    /**
     * 获取数组列表元素个数
     *
     * @return
     */
    public int size() {
        return size;
    }
}

MyArrayList类中主要有如下一些部件:

(1)无参构造器,该构造器创建一个空的数组列表;

(2)含有一个初始容量参数的构造器,该构造器创建一个具有初始容量的数据列表;

(3)一个添加新元素的add方法,该方法每次新增一个元素,在新增元素前对容器容量进行检查和扩容;

(4)一个获取容器容量实际大小的size方法,这里有两个关于大小的概念:一个是容器容量大小,表示容量最多可以存储多少个元素,一个是容器实际大小,表示容器已经存储的元素个数;

(5)一个根据索引index获取元素的get方法,获取前对传入的参数index进行检查;

客户端代码如下:

/**
 * Created by leboop on 2020/5/24.
 */
public class IteratorMain {
    public static void main(String[] args) {
        MyArrayList<String> myArrayList=new MyArrayList<>();
        myArrayList.add("a");
        myArrayList.add("b");
        myArrayList.add("c");
        for(int i=0;i<myArrayList.size();i++){
            String element=myArrayList.get(i);
            System.out.println(element);
        }

    }
}

程序向自定义的数组列表中添加了3个String,并通过get方法进行了遍历。

 

自定义LinkedList

        首先我们自定义一个链表类MyLinkedList,代码如下:

/**
 * Created by leboop on 2020/5/24.
 */
public class MyLinkedList<T> {
    /**
     * 链表的构成:多个节点
     * (1)使用size表示节点的数量
     * (2)使用first表示链表的第一个节点
     * (3)使用last表示链表的最后一个节点
     * 因为节点之间通过Node类中的prev和next进行链接,
     * 所以无论从first节点还是last节点都可以遍历整个链表
     */
    private int size;
    private Node<T> first;
    private Node<T> last;

    public MyLinkedList() {
    }

    public boolean add(T e) {
        /**
         * 创建新的节点:节点构成:
         * (1)数据:e;
         * (2)指向前一个节点的指针prev,指向链表的最后一个节点last
         * (3)指向后一个节点的指针next,后面没有节点,指向null
         */
        final Node<T> prev = last;
        final Node<T> newNode = new Node<>(prev, e, null);
        // 重置链表的last节点,更新为新增的节点
        last = newNode;
        // 如果添加新节点前的链表是空链表,将新节点newNode作为链表的第一个节点
        // 如果添加新节点前的链表不空链表,将原来的最后一个节点的next指针指向新的节点。      
        if (prev == null)
            first = newNode;
        else
            prev.next = newNode;

        size++;

        return true;
    }

    public T get(int index) {
//        参数检查
        if (index < 0 && index >= size)
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
//        遍历节点:
//        (1)如果index没有超过链表的大小的一半,从first向后遍历
//        (2)如果index超过了链表大小的一半,从last向前遍历
//        (3)最后返回节点的数据item
        if (index < (size >> 1)) {
            Node<T> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x.item;
        } else {
            Node<T> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x.item;
        }
    }

    public int size() {
        return size;
    }

    /**
     * 节点的构成:
     * (1)数据
     * (2)指向前一个节点的指针prev;
     * (3)指向后一个节点的指针next;
     *
     * @param <T> 节点的数据类型
     */
    private static class Node<T> {
        //        节点中的数据
        T item;
        //        节点中的指针,指向洗下一个节点
        Node<T> next;
        //        节点中的指针,指向后一个节点
        Node<T> prev;

        //节点初始化
        Node(Node<T> prev, T element, Node<T> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
}

该类包含以下几个部件:

(1)链表是由多个Node构成的,链表的基本属性:链表大小size,链表的第一个节点first,链表的最后一个节点last;

(2)私有内部类Node,Node是链表的基本组成单位,Node也有自己的基本组成:数据item,指向前一个节点的指针prev和指向后一个节点的指针next,链表也是通过这两个指向进行前后连接得到链表;

(3)在原有链表末尾添加新的数据add方法,add方法的本质是添加数据,但添加数据后需要维持链表结构,基本思路是根据新的数据创建一个新的节点,新的节点的prev指针需要指向原链表的最后一个节点last,原链表的最后一个节点的next指针也需要指向新的节点(双向链接)。

(4)获取指定索引位置的数据,基本思路是如果位置在链表的前半部分,则从first节点向后遍历获取,如果位置在链表的后半部分,则从last节点向前遍历获取;

现在客户端代码变为:

/**
 * Created by leboop on 2020/5/24.
 */
public class IteratorMain {
    public static void main(String[] args) {
        MyArrayList<String> myArrayList=new MyArrayList<>();
        myArrayList.add("a");
        myArrayList.add("b");
        myArrayList.add("c");
        for(int i=0;i<myArrayList.size();i++){
            String element=myArrayList.get(i);
            System.out.println(element);
        }

        MyLinkedList<Integer> myLinkedList=new MyLinkedList<>();
        myLinkedList.add(1);
        myLinkedList.add(2);
        myLinkedList.add(3);
        for(int i=0;i<myLinkedList.size();i++){
            Integer element=myLinkedList.get(i);
            System.out.println(element);
        }
    }
}

如果现在有很多的数据结构,客户端代码变得非常冗余。

 

自定义Collection

        将数组列表和链表中的size、add和get方法进行抽象,如下:

/**
 * Created by leboop on 2020/5/24.
 */
public interface MyCollection<T> {
    int size();

    boolean add(T e);

    T get(int index);
}

然后MyLinkedList和MyArrayList实现该接口,关键代码如下:

public class MyArrayList<T> implements MyCollection<T>{
    //略
}
public class MyLinkedList<T> implements MyCollection<T>{
        //略
}

现在客户端就可复用元素添加和遍历操作了,如下:

/**
 * Created by leboop on 2020/5/24.
 */
public class IteratorMain {
    public static void main(String[] args) {
        MyCollection<String> c1=new MyArrayList<>();
        test(c1);
        MyCollection<String> c2=new MyLinkedList<>();
        test(c2);
    }

    public static void test(MyCollection<String> c){
        c.add("a");
        c.add("b");
        c.add("c");
        for(int i=0;i<c.size();i++){
            String element=c.get(i);
            System.out.println(element);
        }
    }
}

复用了test方法。

 

自定义Iterator和Iteratable

        自定义迭代器接口,如下:

/**
 * Created by leboop on 2020/5/24.
 */
public interface MyIterator<T> {
    boolean hasNext();
    T next();
}

 迭代器接口含有两个方法

(1)hasNext方法判断是否含有可以迭代的元素;

(2)next方法迭代元素,并返回;

自定义产生迭代器的接口,如下:

/**
 * Created by leboop on 2020/5/24.
 */
public interface MyIterable<T> {
    MyIterator<T> iterator();
}

MyIterator和MyIterable的区别是:前者是具体的迭代接口,后者是表示是否具备迭代能力,产生迭代器。现在需要MyCollection需要具备迭代能力,所以需要继承MyIterable接口,如下:

/**
 * Created by leboop on 2020/5/24.
 */
public interface MyCollection<T> extends MyIterable<T>{
    int size();

    boolean add(T e);

    T get(int index);

    MyIterator<T> myIterator();
}
此时实现了MyCollection的两个具体的类MyArrayList和MyLinkedList都具备了迭代能力。它们需要具体实现myIterator方法,产生自己的迭代器。MyArrayList迭代器可以如下实现:
/**
 * Created by leboop on 2020/5/24.
 */
public class MyArrayList<T> implements MyCollection<T> {
    //    数组列表大小
    private int size;
    //    数组列表结构,数据列表真正存储的数据
    private Object[] elementData;

    //省略部分代码

    @Override
    public MyIterator<T> myIterator() {
        return new MyArrayListIterator();
    }

    private class MyArrayListIterator implements MyIterator<T> {
        private int currentIndex = 0;

        @Override
        public T next() {
            T o = (T) elementData[currentIndex];
            currentIndex++;

            return o;
        }

        @Override
        public boolean hasNext() {
            if (currentIndex >= size) return false;
            else return true;
        }
    }
}

 MyLinkedList迭代器如下实现:

/**
 * Created by leboop on 2020/5/24.
 */
public class MyLinkedList<T> implements MyCollection<T>{

    private int size;
    private Node<T> first;
    private Node<T> last;
    
    //省略部分代码 

    private static class Node<T> {
        //        节点中的数据
        T item;
        //        节点中的指针,指向洗下一个节点
        Node<T> next;
        //        节点中的指针,指向后一个节点
        Node<T> prev;

        //节点初始化
        Node(Node<T> prev, T element, Node<T> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    @Override
    public MyIterator<T> myIterator() {
        return new MyIterator<T>(){
            Node currentNode = first;

            @Override
            public T next() {
                T o = (T)currentNode.item;
                currentNode = currentNode.next;
                
                return o;
            }

            @Override
            public boolean hasNext() {
                if(currentNode==null) return false;
                else return true;
            }
        };
    }
}

现在客户端无论是数组列表还是链表,或者其他数据结构,都可以先创建一个迭代器,使用迭代器对这些数据进行遍历。

/**
 * Created by leboop on 2020/5/24.
 */
public class IteratorMain {
    public static void main(String[] args) {
        MyCollection<String> c=new MyArrayList<>();
        c.add("a");
        c.add("b");
        c.add("c");
        MyIterator<String> iterator=c.myIterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leboop-L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值