Java函数式编程(十二):监控文件修改

使用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表达式进行软件的设计。

(0)

相关推荐

  • java使用WatchService监控文件夹示例

    通过java7提供的WatchService API 实现对文件夹的监控 package service; import config.Config; import java.io.IOException; import java.nio.file.*; import java.util.List; import java.util.concurrent.TimeUnit; public class WatchDirService { private WatchService watchServ

  • Java实现文件变化监控

    一. spring配置文件: application.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://w

  • Java实时监控日志文件并输出的方法详解

    前言 最近有一个银行数据漂白系统,要求操作人员在页面调用远端Linux服务器的shell,并将shell输出的信息保存到一个日志文件,前台页面要实时显示日志文件的内容.这个问题难点在于如何判断哪些数据是新增加的,通过查看JDK 的帮助文档, java.io.RandomAccessFile可以解决这个问题.为了模拟这个问题,编写LogSvr和 LogView类,LogSvr不断向mock.log日志文件写数据,而 LogView则实时输出日志变化部分的数据. 代码1:日志产生类 package

  • Java使用WatchService监控文件内容变化的示例

    场景 系统实现中经常需要能够感知配置文件的变化,然后及时更新上下文. 实现方案 自己起一个单独线程,定时加载文件,实现较简单,但是无法保证能够实时捕捉文件变化,同时耗CPU 使用commons-io中的 FileAlterationObserver,思想和上面类似,对比前后文件列表的变化,触发对应事件 JDK 1.7提供的WatchService,利用底层文件系统提供的功能 使用 WatchService WatchService用来监控一个目录是否发生改变,但是可以通过 WatchEvent

  • java 使用线程监控文件目录变化的实现方法

    java 使用线程监控文件目录变化的实现方法 由于某种特殊的需求.弄了个使用线程监控文件目录变化的 代码基本如下.其中减去一些复杂的操作.只留下基本代码: package com.file; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; public

  • java实现文件变化监控的方法(推荐)

    一. spring配置文件:application.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://ww

  • Java函数式编程(十二):监控文件修改

    使用flatMap列出子目录 前面已经看到如何列出指定目录下的文件了.我们再来看下如何遍历指定目录的直接子目录(深度为1),先实现一个简单的版本,然后再用更方便的flatMap()方法来实现. 我们先用传统的for循环来遍历一个指定的目录.如果子目录中有文件,就添加到列表里:否则就把子目录添加到列表里.最后,打印出所有文件的总数.代码在下面--这个是困难模式的. 复制代码 代码如下: public static void listTheHardWay() {      List<File> f

  • Java函数式编程(十):收集器

    前面我们已经用过几次collect()方法来将Stream返回的元素拼成ArrayList了.这是一个reduce操作,它对于将一个集合转化成另一种类型(通常是一个可变的集合)非常有用.collect()函数,如果和Collectors工具类里的一些方法结合起来使用的话,能提供极大的便利性,本节我们将会介绍到. 我们还是继续使用前面的Person列表作为例子,来看一下collect()方法到底有哪些能耐.假设我们要从原始列表中找出所有大于20岁的人.下面是使用了可变性和forEach()方法实现

  • 详解Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于某种语法或调用API去进行编程.例如,我们现在需要从一组数字中,找出最小的那个数字,若使用用命令式编程实现这个需求的话,那么所编写的代码如下: public static void main(String[] args) { int[] nums = new int[]{1, 2, 3, 4, 5,

  • Java 函数式编程梳理

    目录 一.Lambda表达式 1.1 函数式编程思想概述 1.2 体验Lambda表达式 1.3 Lambda表达式的标准格式 1.4 Lambda表达式的练习 1.5 Lambda表达式的省略模式 1.6 Lambda表达式的注意事项 1.7 Lambda表达式和匿名内部类的区别 二.接口组成更新 1.1 接口组成更新概述 1.2 接口中默认方法 1.3 接口中静态方法 1.4 接口中私有方法 三.方法引用 1.1 体验方法引用 1.2 方法引用符 1.3 Lambda表达式支持的方法引用 1

  • 详解JAVA 函数式编程

    1.函数式接口 1.1概念: java中有且只有一个抽象方法的接口. 1.2格式: 修饰符 interface 接口名称 { public abstract 返回值类型 方法名称(可选参数信息); // 其他非抽象方法内容 } //或者 public interface MyFunctionalInterface { void myMethod(); } 1.3@FunctionalInterface注解: 与 @Override 注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解

  • Java 函数式编程要点总结

    目录 一.函数式概念 二.函数与方法 三.JDK函数基础 1.Lambda表达式 2.函数式接口 四.Optional类 1.Null判断 2.Optional应用 五.Stream流 六.源代码地址 一.函数式概念 函数式编程是一种结构化编程的范式,主要思想是把运算过程尽量写成系列嵌套的函数调用.函数编程的概念表述带有很抽象的感觉,可以基于案例看: public class Function01 {     public static void main(String[] args) {   

  • java.nio.file.WatchService 实时监控文件变化的示例代码

    目录 1.示例代码 2.其实并没有实时 在平时的开发过程中,会有很多场景需要实时监听文件的变化,如下:1.通过实时监控 mysql 的 binlog 日志实现数据同步2.修改配置文件后,希望系统可以实时感知3.应用系统将日志写入文件中,日志监控系统可以实时抓取日志,分析日志内容并进行报警4.类似 ide 工具,可以实时感知管理的工程下的文件变更 在 Java 语言中,从 JDK7 开始,新增了java.nio.file.WatchService类,用来实时监控文件的变化. 1.示例代码 File

  • Java以编程方式实现JAR文件的创建

    目录 Jar文件的解剖 创建一个 JarTool 类 JAR描述符 打开JAR文件进行编写 将文件添加到JAR中 一个工作实例 总结 大家好,我是指北君. 在这篇文章中,我们将介绍一下以编程方式创建jar文件的过程.在编写软件时,最终我们需要将其部署到生产状态.在某些情况下,使用带有独立文件的classpath是可以的.通常情况下,处理一个文件会更方便.在Java的情况下,标准的方法是使用JAR.WAR或EAR文件. 基本过程是编写清单,打开jar,添加内容,最后保存jar. Jar文件的解剖

  • Java函数式编程(二):集合的使用

    第二章:集合的使用 我们经常会用到各种集合,数字的,字符串的还有对象的.它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多.在这章中,我们探索下如何使用lambda表达式来操作集合.我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并. 遍历列表 遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化.我们使用一个遍历名字的小例子,从最古老的版本介绍到现在最优雅的版本. 用下面的代码我们很容易创建一个不可变的名字的列表: 复制代码 代码如下:

  • Java函数式编程(一):你好,Lambda表达式

    第一章 你好,lambda表达式! 第一节 Java的编码风格正面临着翻天覆地的变化. 我们每天的工作将会变成更简单方便,更富表现力.Java这种新的编程方式早在数十年前就已经出现在别的编程语言里面了.这些新特性引入Java后,我们可以写出更简洁,优雅,表达性更强,错误更少的代码.我们可以用更少的代码来实现各种策略和设计模式. 在本书中我们将通过日常编程中的一些例子来探索函数式风格的编程.在使用这种全新的优雅的方式进行设计编码之前,我们先来看下它到底好在哪里. 改变了你的思考方式 命令式风格--

随机推荐