Java函数式编程(十二):监控文件修改
作者:有孚 发布时间:2022-08-11 10:21:01
使用flatMap列出子目录
前面已经看到如何列出指定目录下的文件了。我们再来看下如何遍历指定目录的直接子目录(深度为1),先实现一个简单的版本,然后再用更方便的flatMap()方法来实现。
我们先用传统的for循环来遍历一个指定的目录。如果子目录中有文件,就添加到列表里;否则就把子目录添加到列表里。最后,打印出所有文件的总数。代码在下面——这个是困难模式的。
public static void listTheHardWay() {
List<File> files = new ArrayList<>();
File[] filesInCurrentDir = new File(".").listFiles();
for(File file : filesInCurrentDir) {
File[] filesInSubDir = file.listFiles();
if(filesInSubDir != null) {
files.addAll(Arrays.asList(filesInSubDir));
} else {
files.add(file);
}
}
System.out.println("Count: " + files.size())
}
我们先获取当前目录下的文件列表,然后进行遍历。对于每个文件,如果它有子文件,就把它们添加到列表中。这样做是没问题的,不过它有一些常见的问题:可变性,基本类型偏执,命令式,代码冗长,等等。一个叫flatMap()的小方法就可以解决掉这些问题。
正如这个名字所说的,这个方法在映射后会进行扁平化。它会像map()一样对集合中的元素进行映射。但是和map()方法不同的是,map()方法里面的lambda表达式只是返回一个元素,而这里返回的是一个Stream对象。于是这个方法将多个流压平,将里面的每个元素映射到一个扁平化的流中。
我们可以用flatMap()来执行各种操作,不过现在手头的这个问题就正好诠释了它的价值。每个子目录都有一个文件的列表或者说流,而我们希望获取当前目录下的所有子目录中的文件列表。
有一些目录可能是空的,或者说没有子元素。这种情况下,我们将这个空目录或者文件包装成一个流对象。如果我们想忽略某个文件,JDK中的flatMap()方法也可以很好的处理空文件;它会把一个空引用作为一个空集合合并到流里。来看下flatMap()方法的使用。
public static void betterWay() {
List<File> files =
Stream.of(new File(".").listFiles())
.flatMap(file -> file.listFiles() == null ?
Stream.of(file) : Stream.of(file.listFiles()))
.collect(toList());
System.out.println("Count: " + files.size());
}
我们先是获取了当前目录的子文件流,然后调用了它的flatMap()方法。然后将一个lambda表达式传给这个方法,这个表达式会返回指定文件的子文件的流。flatMap()方法返回的的是当前目录所有子目录下的文件的集合。我们使用collect()方法以及Collectors里面的toList()(方法把它们收集到一个列表中。
我们传给flatMap()的这个lambda表达式,它返回的是一个文件的子文件。 如果没有的话,则返回这个文件的流。flatMap()方法优雅地将这个流映射到一个流的集合中,然后将这个集合扁平化,最终合并到一个流中。
flatMap()方法减少了许多开发的工作——它将两个连续的操作很好的结合到了一起,这通常称为元组 ——用一个优雅的操作就完成了。
我们已经知道如何使用flatMap()方法来将直接子目录中的所有文件列出来。下面我们来监控一下文件的修改操作。
监控文件修改
我们已经知道如何查找文件及目录,不过如果我们希望在文件创建,修改或删除的时候,能够接收到提示消息的话,这个也非常简单。这样的机制对于监视一些特殊文件比如配置文件,系统资源的改动非常有用。下面我们来探索下Java 7中引入的这个工具,WatchService,它可以用来监控文件的修改。下面我们看到的许多特性都来自JDK 7,而这里最大的改进就是内部迭代器带来的便利性。
我们先来写个监控当前目录中的文件修改的例子。JDK中的Path类会对应文件系统中的一个实例,它是一个观察者服务的工厂。我们可以给这个服务注册通知事件,就像这样:
inal Path path = Paths.get(".");
final WatchService watchService =
path.getFileSystem()
.newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("Report any file changed within next 1 minute...");
我们注册了一个WatchService来观察当前目录的修改。你可以轮询这个WatchService来获取目录下文件的修改操作,它会通过一个WatchKey将这些改动返回给我们。一旦我们拿到了这个key,可以遍历它的所有事件来获取文件更新的详细信息。因为可能会有多个文件被同时修改,poll操作可能会返回多个事件。来看下轮询以及遍历的代码。
final WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
if(watchKey != null) {
watchKey.pollEvents()
.stream()
.forEach(event ->
System.out.println(event.context()));
}
这里可以看到,Java 7和Java 8的特性同时出场了。我们把pollEvents()返回的集合转化成了一个Java 8的Stream,然后使用它的内部迭代器来打印出每个文件的详细的更新信息。
我们来运行下这段代码,然后将当前目录下的sample.txt文件修改一下,看下这个程序是否能察觉这个更新。
Report any file changed within next 1 minute...
sample.txt
当我们修改了这个文件的时候,程序会提示说文件被修改了。我们可以用这个功能来监视不同文件的更新,然后执行相应的任务。当然我们也可以只注册文件新建或者删除的操作。
总结
有了lambda表达式和方法引用后,像字符串及文件的操作,创建自定义比较器这些常见的任务都变得更简单也更简洁了。匿名内部类也变得优雅起来了,而可变性就像日出后的晨雾一样,也消失得无影无踪了。使用这种新风格进行编码还有一个福利,就是你可以使用JDK的新设施来高效地遍历庞大的目录。
现在你已经知道如何创建lambda表达式并把它传递给方法了。下一章我们会介绍如何使用函数式接口及lambda表达式进行软件的设计。


猜你喜欢
- 导入表格的主要思路就是:首先从前端页面上传文件,这里先区分一下Multipartfile和File,前者代表的是HTML中form data
- 关于在spring 容器初始化 bean 和销毁前所做的操作定义方式有三种:第一种:通过注解@PostConstruct 和 @
- 一:将String字符串放在最前面防止发生NullPointerException异常,我们通常把String字符串放在equals方法的左
- 重要属性1、scale: 小数点后的位数。如将1.234构建为BigDecimal,scale属性则为32、RoundingMode(对照数
- 网上关于如何切换,其实说的很明确,本文主要通过profile进行快速切换已实现在不同场合下,用不同的打包方式。jar到war修改步骤pom文
- BeanFactory接口:IoC容器的顶级接口,是IoC容器的最基础实现,也是访问Spring容器的根接口,负责对bean的创建,访问等工
- 本文为大家分享了Android Studio3安装图文教程,供大家参考,具体内容如下Android Studio及其相关资源下载地址:dow
- 一、引言90坦克大战,很经典的一款游戏,当年与小伙伴一人一个手柄,搬上小板凳坐在电视机前,身体时不时跟随手柄摇晃着,时而表情严肃、眉头紧锁,
- 前言一说到Socket,想必大家都或多或少有所涉及,从最初的计算机网络课程,讲述了tcp协议,而Socket就是对协议的进一步封装,使我们开
- 前言首先,啊,先简单介绍一下优先队列的概念,学数据结构以及出入算法竞赛的相信都对队列这一数据结构十分熟悉,这是一个线性的数据结构.针对队列这
- Java原生SPI面向接口编程+策略模式实现建立接口Robotpublic interface Robot { /
- 本文实例讲述了Android实现自定义带文字和图片Button的方法。分享给大家供大家参考。具体分析如下:在Android开发中经常会需要用
- 一、创建springboot项目(采用骨架方式)创建完成;我们分析下pom文件中内容:所使用到的关键依赖: <!--springBoo
- 继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节
- 近和朋友完成了一个大单子架构是mvc5+ef6+Bootstrap,用的是vs2015,数据库是sql server2014。朋友做的架构,
- 依赖 <dependency> <gro
- 开发环境安装JDK和JRE下载安装文件并安装:jdk-8u11-windows-i586.exejre-8u11-windows-i586.
- 这篇文章主要介绍了SpringBoot项目的测试类实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要
- 应用场景:在Android开发过程中,有时需要调用手机自身设备的功能,上篇文章主要侧重摄像头拍照功能的调用。本篇文章将综合实现拍照与视频的操
- 一、什么是模板方法设计模式从字面意义上理解, 模板方法就是定义出来一套方法, 作为模板, 也就是基础。 在这个基础上, 我们可以进行加工,实