iterable接口

整个接口框架关系如下(来自百度百科):

iterable接口其实是java集合大家庭的最顶级的接口之一了,实现这个接口,可以视为拥有了获取迭代器的能力。Iterable接口出现在JDK1.5,那个时候只有iterator()方法,主要是定义了迭代集合内元素的规范。

实现了Iterable接口,我们可以使用增强的for循环,即

  1. for(String str : lists){
  2. System.out.println(str);
  3. }

1. 内部定义的方法

java集合最源头的接口,实现这个接口的作用主要是集合对象可以通过迭代器去遍历每一个元素。

源码如下:

  1. // 返回一个内部元素为T类型的迭代器(JDK1.5只有这个接口)
  2. Iterator<T> iterator();
  3. // 遍历内部元素,action意思为动作,指可以对每个元素进行操作(JDK1.8添加)
  4. default void forEach(Consumer<? super T> action) {}
  5. // 创建并返回一个可分割迭代器(JDK1.8添加),分割的迭代器主要是提供可以并行遍历元素的迭代器,可以适应现在cpu多核的能力,加快速度。
  6. default Spliterator<T> spliterator() {
  7. return Spliterators.spliteratorUnknownSize(iterator(), 0);
  8. }

从上面可以看出,foreach迭代以及可分割迭代,都加了default关键字,这个是Java 8 新的关键字,以前接口的所有接口,具体子类都必须实现,而对于deafult关键字标识的方法,其子类可以不用实现,这也是接口规范发生变化的一点。

下面我们分别展示三个接口的调用:

1.1 iterator()方法

iterator()方法,是接口中的核心方法,主要是获取迭代器,获取到的iteratornext(),hasNext(),remove()等方法。

  1. public static void iteratorHasNext(){
  2. List<String> list=new ArrayList<String>();
  3. list.add("Jam");
  4. list.add("Jane");
  5. list.add("Sam");
  6. // 返回迭代器
  7. Iterator<String> iterator=list.iterator();
  8. // hashNext可以判断是否还有元素
  9. while(iterator.hasNext()){
  10. //next()作用是返回当前指针指向的元素,之后将指针移向下个元素
  11. System.out.println(iterator.next());
  12. }
  13. }

当然也可以使用for-each loop方式遍历

  1. for (String item : list) {
  2. System.out.println(item);
  3. }

但是实际上,这种写法在class文件中也是会转成迭代器形式,这只是一个语法糖。class文件如下:

  1. public class IterableTest {
  2. public IterableTest() { }
  3. public static void main(String[] args) {
  4. iteratorHasNext();
  5. }
  6. public static void iteratorHasNext() {
  7. List<String> list = new ArrayList();
  8. list.add("Jam");
  9. list.add("Jane");
  10. list.add("Sam");
  11. Iterator<String> iterator = list.iterator();
  12. Iterator var2 = list.iterator();
  13. while(var2.hasNext()) {
  14. String item = (String)var2.next();
  15. System.out.println(item);
  16. }
  17. }
  18. }

需要注意的一点是,迭代遍历的时候,如果删除或者添加元素,都会抛出修改异常,这是由于快速失败【fast-fail】机制。

  1. public static void iteratorHasNext(){
  2. List<String> list=new ArrayList<String>();
  3. list.add("Jam");
  4. list.add("Jane");
  5. list.add("Sam");
  6. for (String item : list) {
  7. if(item.equals("Jam")){
  8. list.remove(item);
  9. }
  10. System.out.println(item);
  11. }
  12. }

从下面的错误我们可以看出,第一个元素是有被打印出来的,也就是remove操作是成功的,只是遍历到第二个元素的时候,迭代器检查,发现被改变了,所以抛出了异常。

  1. Jam
  2. Exception in thread "main" java.util.ConcurrentModificationException
  3. at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
  4. at java.util.ArrayList$Itr.next(ArrayList.java:859)
  5. at IterableTest.iteratorHasNext(IterableTest.java:15)
  6. at IterableTest.main(IterableTest.java:7)

1.2 forEach()方法

其实就是把对每一个元素的操作当成了一个对象传递进来,对每一个元素进行处理。

  1. default void forEach(Consumer<? super T> action) {
  2. Objects.requireNonNull(action);
  3. for (T t : this) {
  4. action.accept(t);
  5. }
  6. }

当然像ArrayList自然也是有自己的实现的,那我们就可以使用这样的写法,简洁优雅。forEach方法在java8中参数是java.util.function.Consumer,可以称为消费行为或者说动作类型。

  1. list.forEach(x -> System.out.print(x));

同时,我们只要实现Consumer接口,就可以自定义动作,如果不自定义,默认迭代顺序是按照元素的顺序。

  1. public class ConsumerTest {
  2. public static void main(String[] args) {
  3. List<String> list=new ArrayList<String>();
  4. list.add("Jam");
  5. list.add("Jane");
  6. list.add("Sam");
  7. MyConsumer myConsumer = new MyConsumer();
  8. Iterator<String> it = list.iterator();
  9. list.forEach(myConsumer);
  10. }
  11. static class MyConsumer implements Consumer<Object> {
  12. @Override
  13. public void accept(Object t) {
  14. System.out.println("自定义打印:" + t);
  15. }
  16. }
  17. }

输出的结果:

  1. 自定义打印:Jam
  2. 自定义打印:Jane
  3. 自定义打印:Sam

1.3 spliterator()方法

这是一个为了并行遍历数据元素而设计的迭代方法,返回的是Spliterator,是专门并行遍历的迭代器。以发挥多核时代的处理器性能,java默认在集合框架中提供了一个默认的Spliterator实现,底层也就是Stream.isParallel()实现的,我们可以看一下源码:

  1. // stream使用的就是spliterator
  2. default Stream<E> stream() {
  3. return StreamSupport.stream(spliterator(), false);
  4. }
  5. default Spliterator<E> spliterator() {
  6. return Spliterators.spliterator(this, 0);
  7. }
  8. public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
  9. Objects.requireNonNull(spliterator);
  10. return new ReferencePipeline.Head<>(spliterator,
  11. StreamOpFlag.fromCharacteristics(spliterator),
  12. parallel);
  13. }

使用的方法如下:

  1. public static void spliterator(){
  2. List<String> list = Arrays.asList("1", "2", "3","4","5","6","7","8","9","10");
  3. // 获取可迭代器
  4. Spliterator<String> spliterator = list.spliterator();
  5. // 一个一个遍历
  6. System.out.println("tryAdvance: ");
  7. spliterator.tryAdvance(item->System.out.print(item+" "));
  8. spliterator.tryAdvance(item->System.out.print(item+" "));
  9. System.out.println("\n-------------------------------------------");
  10. // 依次遍历剩下的
  11. System.out.println("forEachRemaining: ");
  12. spliterator.forEachRemaining(item->System.out.print(item+" "));
  13. System.out.println("\n------------------------------------------");
  14. // spliterator1:0~10
  15. Spliterator<String> spliterator1 = list.spliterator();
  16. // spliterator1:6~10 spliterator2:0~5
  17. Spliterator<String> spliterator2 = spliterator1.trySplit();
  18. // spliterator1:8~10 spliterator3:6~7
  19. Spliterator<String> spliterator3 = spliterator1.trySplit();
  20. System.out.println("spliterator1: ");
  21. spliterator1.forEachRemaining(item->System.out.print(item+" "));
  22. System.out.println("\n------------------------------------------");
  23. System.out.println("spliterator2: ");
  24. spliterator2.forEachRemaining(item->System.out.print(item+" "));
  25. System.out.println("\n------------------------------------------");
  26. System.out.println("spliterator3: ");
  27. spliterator3.forEachRemaining(item->System.out.print(item+" "));
  28. }
  • tryAdvance() 一个一个元素进行遍历
  • forEachRemaining() 顺序地分块遍历
  • trySplit()进行分区形成另外的 Spliterator,使用在并行操作中,分出来的是前面一半,就是不断把前面一部分分出来

结果如下:

  1. tryAdvance:
  2. 1 2
  3. -------------------------------------------
  4. forEachRemaining:
  5. 3 4 5 6 7 8 9 10
  6. ------------------------------------------
  7. spliterator1:
  8. 8 9 10
  9. ------------------------------------------
  10. spliterator2:
  11. 1 2 3 4 5
  12. ------------------------------------------
  13. spliterator3:
  14. 6 7

还有一些其他的用法在这里就不列举了,主要是trySplit()之后,可以用于多线程遍历。理想的时候,可以平均分成两半,有利于并行计算,但是不是一定平分的。

总结

以上可以得知,iterable接口,主要是定义了迭代遍历的规范,这个接口的作用是获取迭代器,迭代器在JDK1.8版本增加了可分割迭代器,更有利于并发处理。iterable接口,从字面意义来说,就是可以迭代的意思,可以理解为实现这个接口的集合类获得了迭代遍历的能力,同时它也是集合的顶级接口,Collection接口继承了它。

此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~

技术之路不在一时,山高水长,纵使缓慢,驰而不息。

公众号:秦怀杂货店

Java集合【3】-- iterable接口超级详细解析的更多相关文章

  1. Java集合【7】--List接口超级详细解析

    目录 1.List接口的特性 2.List接口的源码解析 3.相关子类介绍 3.1 ArrayList 3.1.1 成员变量 3.1.2 构造方法 3.1.3 常用增删改查方法 添加元素 查询元素 更 ...

  2. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  3. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  4. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  5. 【转】Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  6. Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  7. 【转】 Java 集合系列07之 Stack详细介绍(源码解析)和使用示例

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  8. (一)java集合框架——Iterable

    Iterable接口是java 集合框架的顶级接口,实现此接口使集合对象可以通过迭代器遍历自身元素,我们可以看下它的成员方法 修饰符和返回值 方法名 描述 Iterator<T> iter ...

  9. Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

    概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解Arra ...

随机推荐

  1. Java学习的第五十二天

    1.例9.4对象数组的使用方法 public class Cjava { public static void main(String[]args) { Box b[] = {new Box(10,1 ...

  2. python数据类型互相转换

    类型转换 关注公众号"轻松学编程"了解更多. 主要针对几种存储工具:list.tuple.dict.set 特殊之处:dict是用来存储键值对的. 1.list 转换为set l1 ...

  3. 模板——Splay

    $Splay$ #include <bits/stdc++.h> #define inf (int)1e9 using namespace std; const int N=1e5+100 ...

  4. ERP的权限管理的操作与设计--开源软件诞生24

    赤龙ERP用户与权限管理讲解--第24篇 用日志记录"开源软件"的诞生 [进入地址 点亮星星]----祈盼着一个鼓励 博主开源地址: 码云:https://gitee.com/re ...

  5. 纯css实现箭头

    很久之前收集的,忘记出处了. 1.梯形: 当元素宽.高和边框的宽相近(等)时,改变某一边的颜色可以看到一个梯形: border: 10px solid #000; border-left-color: ...

  6. inno setup win10 创建菜单里面卸载图标

    1.win10自己注册表关联的卸载图标 会隐藏 卸载图标.现在的项目法是 不写注册表 直接 在目标文件里面创建快捷方式 移动到菜单里面 ; 脚本由 Inno Setup 脚本向导 生成! ; 有关创建 ...

  7. php生成gitbook路径

    public function file_list() { $path='文件路径'; $arr=scandir($path); $i=0; foreach ($arr as $k=>$v) { ...

  8. leetcode114:valid-sudoku

    题目描述 根据数独的规则Sudoku Puzzles - The Rules.判断给出的局面是不是一个符合规则的数独局面 数独盘面可以被部分填写,空的位置用字符'.'.表示 这是一个部分填写的符合规则 ...

  9. php正则匹配整数

    <?php if(!preg_match('/^([1-9][0-9]*){1,10}$/',$buy_sku)) { $error['content'] = '请检查库存格式'; echo j ...

  10. JS中的Array之方法(1)

    a=[2,4,5,6,7,90]; [1]. a.toString();  // 返回字符串表示的数组,逗号分隔 "2,4,5,6,7,90" [2]. a.join('||'); ...