Java容器(一)之Iterator与Iterable接口

来源:互联网 发布:html与css javascript 编辑:程序博客网 时间:2024/06/07 19:57

Java为了方便编程,预定义了一些特定功能的接口,本文旨在保存和说明这些接口的功能和作用,也会尽量配合源码进行说明,这个会分成多篇文章进行说明,希望大家能够从中获得自己想要的知识。
本文中涉及到的接口:

Iterator<E>Iterable<E>

一、Iterator<E>接口
1、简介:Iterator接口的主要目的是让我们自定义的类具有循环的能力,这个接口并不复杂,完全可以自己实现
2、源码:

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());    }}

Iterator的接口一共有4个方法,这里需要注意的是hasNext()和next()的默认修饰词都是public abstact,但是remove()和forEachRemaining(Consumer<? super E>)的修饰词是default,default是SE8新出的特性,用法和修饰词的中文翻译差不多,就是默认,这个接口方法如果加了default修饰词就意味着这个接口的这个方法已经有了默认的实现,这个时候我们就不用再去专门实现它,这个修饰词的主要目的还是使新接口适配旧代码
3、实例:

import java.util.Iterator;class Money implements Iterator<Money>{    Money(Integer i)    {        System.out.println("挣了"+i+"块钱");        this.Salary = i;    }    Money(int i)    {        System.out.println("再花"+i+"块钱");    }    private int Salary;    public boolean hasNext()    {        return Salary-- > 0? true : false;    }    public Money next()    {        return new Money(1);    }}public class Try{    public static void main(String[] args){        Money money = new Money(new Integer(3));        while(money.hasNext()){            money.next();        }        System.out.println("没钱了");    }}

输出为:

挣了3块钱再花1块钱再花1块钱再花1块钱没钱了

二、Iterable<E>接口
1、简介:简单来说Iterable是基于Iterator的又一个封装,实现了Iterable接口就可以使用forEach语法。
2、源码:

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);    }}

根据上面我们对于deafult接口的解释,我们可以看到这个Iterable的两个方法forEach(Consumer<? super T> action),spliterator()实际上都不用我们自己实现,我们只用实现一个返回Iterator<E>的方法。
3、实例:

import java.util.*;class Money implements Iterator<Money>,Iterable<Money>{    public Iterator<Money> iterator(){        System.out.println("调用了iterator()方法");        return new Money(new Integer(3));    }    Money()    {        System.out.println("使用默认构造器");    }    Money(Integer i)    {        System.out.println("挣了"+i+"块钱");        this.Salary = i;    }    Money(int i)    {        System.out.println("再花"+i+"块钱");    }    private int Salary;    public boolean hasNext()    {        System.out.println("调用hasNext()方法");        return Salary-- > 0? true : false;    }    public Money next()    {        System.out.println("调用next()方法");        return new Money(1);    }}public class Try{    public static void main(String[] args){        Money moneys = new Money();        for(Money money : moneys);        //使用了Iterable接口之后就可以使用forEach语法了    }}

输出为:

使用默认构造器调用了iterator()方法挣了3块钱调用hasNext()方法调用next()方法再花1块钱调用hasNext()方法调用next()方法再花1块钱调用hasNext()方法调用next()方法再花1块钱调用hasNext()方法

可以看到Iterator方法实际上是在第一次进入forEach循环中就被调用,随后就循环调用hasNext(),next()方法进行循环。
三、进阶分析:
我们现在要从原理和源码上去理解这两个接口是怎么工作的,先看看Iterator<E>这个接口的这个方法:

default void forEachRemaining(Consumer<? super E> action) {    Objects.requireNonNull(action);    while (hasNext())        action.accept(next());}

这是个默认的接口方法,但是其实一开始看到这个代码我是很懵的,我们先来一步一步来看,首先看看他的参数类型:Consumer<? super E>这个Consumer肯定也是SE8标准库的代码的一部分,源码如下:

@FunctionalInterfacepublic interface Consumer<T> {    void accept(T t);    default Consumer<T> andThen(Consumer<? super T> after) {        Objects.requireNonNull(after);        return (T t) -> { accept(t); after.accept(t); };    }}

Cosumer原来也是个接口,他一共有两个方法,void accept(T)需要我们自己定义,Consumer<T> addThenConsumer<? super T>()方法是默认方法。
这个接口上面有一个注释@FunctionalInterface,这个注释是为了标明这个接口是一个函数式接口函数式接口意味着这个接口下面有且只有一个抽象方法,当然可以有多个域。特别需要注意接口下可以有多个default修饰的方法,像下面这样是可以的:

@FunctionalInterfaceinterface fi{    int i = 1;    void show();    default void show(int i){};}

要自定义一个函数接口并调用比较麻烦,我们首先需要在接口中定义一个且之定义一个我理解为目标方法的的这么一种方法,这个方法不管是在接口开始定义的时候,还是在运行过程中都不需要实际的定义,我定义的接口如下:

interface FI<T>{    boolean test(T i);    default FI<T> show()    {        return (i)->test(i);    };}

调用接口时也比较麻烦,因为我们不能直接像下面这样调用:

FI.test(...)

因为这个时候接口中的test方法还没有被定义,所以我们需要一种另外一种方法来调用这个接口:

public static boolean test(FI<Integer> fi, Integer n){    return fi.test(n);}

先定义一个静态方法,然后从静态方法中调用这个函数式接口:

if(Try.test( n->n>3 , 4)){    System.out.print("good");}

这样就完成了对函数式接口的调用,所有代码如下:

@FunctionalInterfaceinterface FI{    boolean test(int i);    default FI show()    {        return (i)->test(i);    };}public class Try{    public static boolean test(FI fi, int n)    {        return fi.test(n);    }    public static void main(String[] agrs)    {        if(Try.test( n->n>3 , 4))        {            System.out.print("good");        }    }}

需要注意的是,不管在接口中间插入任何代码都无法运行,比如:

default FI<T> show()    {        System.out.println("work");        return (i)->test(i);    };

这个work是怎么样都不会被打印出来的。
但是具体Interable中的forEach方法和Iterator中的forEachRemaining()方法是如何在底层调用的,先留个白