将递归文件系统遍历转换为流
在学习编程的时候,回到Turbo Pascal时代,我设法使用FindFirst
, FindNext
和FindClose
函数在目录中列出文件。 首先,我想出了一个打印给定目录内容的过程。 您可以想象我为能够真正从自身调用该过程以递归遍历文件系统而感到自豪。 好吧,那时我还不知道递归一词,但是它确实有用。 Java中的类似代码如下所示:
public void printFilesRecursively(final File folder) {for (final File entry : listFilesIn(folder)) {if (entry.isDirectory()) {printFilesRecursively(entry);} else {System.out.println(entry.getAbsolutePath());}}
}private File[] listFilesIn(File folder) {final File[] files = folder.listFiles();return files != null ? files : new File[]{};
}
不知道File.listFiles()
可以返回null
,是吗? 这就是它发出I / O错误的信号,就像IOException
不存在一样。 但这不是重点。 System.out.println()
很少是我们所需要的,因此该方法既不可重用也不可组合。 这可能是“ 打开/关闭”原理的最佳反例。 我可以想象文件系统递归遍历的几个用例:
- 获取所有文件的完整列表以供显示
- 查找与给定模式/属性匹配的所有文件(还要检出
File.list(FilenameFilter)
) - 搜索一个特定文件
- 处理每个文件,例如通过网络发送
上面的每个用例都有一系列独特的挑战。 例如,我们不想建立所有文件的列表,因为在开始处理它之前将花费大量的时间和内存。 我们希望通过流水线计算(但没有笨拙的访问者模式)来处理文件的发现和延迟。 另外,我们还希望使搜索短路以避免不必要的I / O。 幸运的是,在Java 8中,其中一些问题可以通过流解决:
final File home = new File(FileUtils.getUserDirectoryPath());
final Stream<Path> files = Files.list(home.toPath());
files.forEach(System.out::println);
请记住, Files.list(Path)
(Java 8中的新增功能)没有考虑子目录,我们将在以后进行修复。 这里最重要的一课是: Files.list()
返回Stream<Path>
–我们可以传递,组合,映射,过滤等的值。它非常灵活,例如,计算我拥有的文件数非常简单在每个扩展名的目录中:
import org.apache.commons.io.FilenameUtils;//...final File home = new File(FileUtils.getUserDirectoryPath());
final Stream<Path> files = Files.list(home.toPath());
final Map<String, List<Path>> byExtension = files.filter(path -> !path.toFile().isDirectory()).collect(groupingBy(path -> getExt(path)));byExtension.forEach((extension, matchingFiles) ->System.out.println(extension + "\t" + matchingFiles.size()));//...private String getExt(Path path) {return FilenameUtils.getExtension(path.toString()).toLowerCase();
}
好吧,您可能会说,只是另一个API。 但是一旦我们需要更深入 ,递归遍历子目录,它就会变得非常有趣。 流的一项惊人功能是,您可以通过各种方式将它们相互组合。 老Scala说“ flatMap that shit”在这里也适用,请查看以下递归Java 8代码:
//WARNING: doesn't compile, yet:private static Stream<Path> filesInDir(Path dir) {return Files.list(dir).flatMap(path ->path.toFile().isDirectory() ?filesInDir(path) :singletonList(path).stream());
}
由filesInDir()
延迟生成的Stream<Path>
包含目录中的所有文件,包括子目录。 您可以通过调用map()
, filter()
, anyMatch()
, findFirst()
等将其用作任何其他流。但是它实际上如何工作? flatMap()
与map()
类似,但是map()
是直接的1:1转换, flatMap()
允许用多个条目替换输入Stream
中的单个条目。 如果使用map()
,则最终会得到Stream<Stream<Path>>
(或者可能是Stream<List<Path>>
)。 但是flatMap()
通过展开内部条目来展平该结构。 让我们看一个简单的例子。 想象Files.list()
返回两个文件和一个目录。 对于文件, flatMap()
随该文件一起接收一个元素的流。 我们不能简单地返回该文件,而必须对其进行包装,但实际上这是无操作的。 对于目录,它变得更加有趣。 在这种情况下,我们递归地调用filesInDir()
。 结果,我们获得了该目录的内容流,并将其注入到外部流中。
上面的代码简短,甜美,并且…无法编译。 这些讨厌的人再次检查了异常。 这是一个固定的代码,包装检查过的异常以保持理智:
public static Stream<Path> filesInDir(Path dir) {return listFiles(dir).flatMap(path ->path.toFile().isDirectory() ?filesInDir(path) :singletonList(path).stream());
}private static Stream<Path> listFiles(Path dir) {try {return Files.list(dir);} catch (IOException e) {throw Throwables.propagate(e);}
}
不幸的是,这种相当优雅的代码还不够懒。 flatMap()
急切求值,因此即使我们几乎不要求第一个文件,它始终会遍历所有子目录。 您可以尝试使用我的小型LazySeq
库,该库尝试提供甚至更lazy-seq
抽象,类似于Scala中的流或Clojure中的lazy-seq
。 但是,即使是标准的JDK 8解决方案也可能确实有用,并且可以大大简化您的代码。
翻译自: https://www.javacodegeeks.com/2014/07/turning-recursive-file-system-traversal-into-stream.html
将递归文件系统遍历转换为流相关推荐
- c 遍历文件 递归遍历_将递归文件系统遍历转换为流
c 遍历文件 递归遍历 在学习编程的时候,回溯到Turbo Pascal的时代,我设法使用FindFirst , FindNext和FindClose函数在目录中列出文件. 首先,我想出了一个打印给定 ...
- 二叉树的几种递归和非递归式遍历:
二叉树的几种递归和非递归式遍历: 1 #include <fstream> 2 #include <iostream> 3 4 using namespace std; 5 6 ...
- 九十五、二叉树的递归和非递归的遍历算法模板
@Author:Runsen 刷Leetcode,需要知道一定的算法模板,本次先总结下二叉树的递归和非递归的遍历算法模板. 二叉树的四种遍历方式,前中后加上层序遍历.对于二叉树的前中后层序遍历,每种遍 ...
- java联接pg库_成为Java流大师–第5部分:将联接的数据库表转换为流
java联接pg库 是否可以将联接的数据库表转换为Java Stream? 答案是肯定的. 由于我们已经多次提出这个问题,因此我们决定写另一篇动手实验文章,说明如何执行更高级的Stream Joins ...
- 成为Java流大师–第5部分:将联接的数据库表转换为流
是否可以将联接的数据库表转换为Java Stream? 答案是肯定的. 既然我们已经多次提出这个问题,我们决定写另一篇动手实验文章,解释如何执行更高级的Stream Joins. 因此,这里是第六篇中 ...
- java 递归深度优先遍历_Java基础 - 二叉树的遍历之深度优先遍历(递归遍历)
package com.yc.test; import java.util.ArrayList; import java.util.List; import com.yc.tree.ThreeLink ...
- java实现递归层次遍历_Java实现二叉树的前序、中序、后序、层序遍历(递归方法)...
在数据结构中,二叉树是树中我们见得最多的,二叉查找树可以加速我们查找的效率,那么输出一个二叉树也变得尤为重要了. 二叉树的遍历方法分为四种,分别为前序遍历.中序遍历.后序.层序遍历.下图即为一个二叉树 ...
- php无嵌套遍历多维数组,不递归怎么遍历多维数组(维数不定)
不递归如何遍历多维数组(维数不定) 现有数组 $tree = array ( array ( 'ID' => 1, 'PARENT' => 0, 'NAME' => '祖父', 'C ...
- 查找树的指定层级_非递归层次遍历方法实现二叉树中指定节点的层次数查找
数据结构教材中,提供了基于队列实现一个二叉树的非递归层次遍历算法.但对于一个任意二叉树,如果要查找其中任何一个节点所在的层次数,教科书中并没有给出基于层次遍历的非递归算法.鉴于层次遍历算法比较容易理解 ...
最新文章
- Radware:当前,CDN安全远远不足
- 【转】Android 快捷方式的创建
- LSM树——放弃读能力换取写能力,将多次修改放在内存中形成有序树再统一写入磁盘...
- linux如何登陆oracle?如何停止、启动oracle和其监听?
- Leetcode 255. Verify Preorder Sequence in Binary Search Tree
- MII/MDIO接口详解(转)
- 《Apache Kafka实战》读书笔记-调优Kafka集群
- Python 第三方库之docx
- 论文浅尝 | 通过共享表示和结构化预测进行事件和事件时序关系的联合抽取
- linux kdb内核调试器,使用KDB调试工具
- linux编辑器终端,分享|尝试将 Jed 作为你的 Linux 终端文本编辑器
- 最全电商分类信息(04)
- 简单明了的java反射机制
- 【网页设计自习室#004】网页页面导航栏(header头部)的设计
- 妈妈,我以后也要上南邮!
- C语言可以应用在哪些领域?
- 罗彻斯特大学计算机科学系专业排名,罗切斯特大学排名计算机工程,得用心去看...
- 解决Google浏览器打开页面速度太慢问题
- apollo配置中心
- 2016第七届ACM山东省赛
热门文章
- docker export_docker使用简介
- (转)mybatis热部署加载*Mapper.xml文件,手动刷新*Mapper.xml文件
- (转)数据库可靠性/可用性、稳定性RTO/RPO
- 最小生成树——Kruskal(克鲁斯卡尔)算法
- 不同特权级间代码段的跳转{ 门 + 跳转(jmp + call) + 返回(ret) }
- jwt令牌_JWT令牌的秘密轮换
- hashmap java_Java – HashMap详细说明
- drools dmn_使用Drools的DMN运行时示例
- primefaces_通过OmniFaces缓存组件以编程方式缓存PrimeFaces图表
- scala集合转java_Java,Scala,Guava和Trove集合-它们可以容纳多少数据?