Java函數(shù)式編程(十二):監(jiān)控文件修改
使用flatMap列出子目錄
前面已經(jīng)看到如何列出指定目錄下的文件了。我們再來看下如何遍歷指定目錄的直接子目錄(深度為1),先實現(xiàn)一個簡單的版本,然后再用更方便的flatMap()方法來實現(xiàn)。
我們先用傳統(tǒng)的for循環(huán)來遍歷一個指定的目錄。如果子目錄中有文件,就添加到列表里;否則就把子目錄添加到列表里。最后,打印出所有文件的總數(shù)。代碼在下面——這個是困難模式的。
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())
}
我們先獲取當(dāng)前目錄下的文件列表,然后進(jìn)行遍歷。對于每個文件,如果它有子文件,就把它們添加到列表中。這樣做是沒問題的,不過它有一些常見的問題:可變性,基本類型偏執(zhí),命令式,代碼冗長,等等。一個叫flatMap()的小方法就可以解決掉這些問題。
正如這個名字所說的,這個方法在映射后會進(jìn)行扁平化。它會像map()一樣對集合中的元素進(jìn)行映射。但是和map()方法不同的是,map()方法里面的lambda表達(dá)式只是返回一個元素,而這里返回的是一個Stream對象。于是這個方法將多個流壓平,將里面的每個元素映射到一個扁平化的流中。
我們可以用flatMap()來執(zhí)行各種操作,不過現(xiàn)在手頭的這個問題就正好詮釋了它的價值。每個子目錄都有一個文件的列表或者說流,而我們希望獲取當(dāng)前目錄下的所有子目錄中的文件列表。
有一些目錄可能是空的,或者說沒有子元素。這種情況下,我們將這個空目錄或者文件包裝成一個流對象。如果我們想忽略某個文件,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());
}
我們先是獲取了當(dāng)前目錄的子文件流,然后調(diào)用了它的flatMap()方法。然后將一個lambda表達(dá)式傳給這個方法,這個表達(dá)式會返回指定文件的子文件的流。flatMap()方法返回的的是當(dāng)前目錄所有子目錄下的文件的集合。我們使用collect()方法以及Collectors里面的toList()(方法把它們收集到一個列表中。
我們傳給flatMap()的這個lambda表達(dá)式,它返回的是一個文件的子文件。 如果沒有的話,則返回這個文件的流。flatMap()方法優(yōu)雅地將這個流映射到一個流的集合中,然后將這個集合扁平化,最終合并到一個流中。
flatMap()方法減少了許多開發(fā)的工作——它將兩個連續(xù)的操作很好的結(jié)合到了一起,這通常稱為元組 ——用一個優(yōu)雅的操作就完成了。
我們已經(jīng)知道如何使用flatMap()方法來將直接子目錄中的所有文件列出來。下面我們來監(jiān)控一下文件的修改操作。
監(jiān)控文件修改
我們已經(jīng)知道如何查找文件及目錄,不過如果我們希望在文件創(chuàng)建,修改或刪除的時候,能夠接收到提示消息的話,這個也非常簡單。這樣的機制對于監(jiān)視一些特殊文件比如配置文件,系統(tǒng)資源的改動非常有用。下面我們來探索下Java 7中引入的這個工具,WatchService,它可以用來監(jiān)控文件的修改。下面我們看到的許多特性都來自JDK 7,而這里最大的改進(jìn)就是內(nèi)部迭代器帶來的便利性。
我們先來寫個監(jiān)控當(dāng)前目錄中的文件修改的例子。JDK中的Path類會對應(yīng)文件系統(tǒng)中的一個實例,它是一個觀察者服務(wù)的工廠。我們可以給這個服務(wù)注冊通知事件,就像這樣:
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來觀察當(dāng)前目錄的修改。你可以輪詢這個WatchService來獲取目錄下文件的修改操作,它會通過一個WatchKey將這些改動返回給我們。一旦我們拿到了這個key,可以遍歷它的所有事件來獲取文件更新的詳細(xì)信息。因為可能會有多個文件被同時修改,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()返回的集合轉(zhuǎn)化成了一個Java 8的Stream,然后使用它的內(nèi)部迭代器來打印出每個文件的詳細(xì)的更新信息。
我們來運行下這段代碼,然后將當(dāng)前目錄下的sample.txt文件修改一下,看下這個程序是否能察覺這個更新。
Report any file changed within next 1 minute...
sample.txt
當(dāng)我們修改了這個文件的時候,程序會提示說文件被修改了。我們可以用這個功能來監(jiān)視不同文件的更新,然后執(zhí)行相應(yīng)的任務(wù)。當(dāng)然我們也可以只注冊文件新建或者刪除的操作。
總結(jié)
有了lambda表達(dá)式和方法引用后,像字符串及文件的操作,創(chuàng)建自定義比較器這些常見的任務(wù)都變得更簡單也更簡潔了。匿名內(nèi)部類也變得優(yōu)雅起來了,而可變性就像日出后的晨霧一樣,也消失得無影無蹤了。使用這種新風(fēng)格進(jìn)行編碼還有一個福利,就是你可以使用JDK的新設(shè)施來高效地遍歷龐大的目錄。
現(xiàn)在你已經(jīng)知道如何創(chuàng)建lambda表達(dá)式并把它傳遞給方法了。下一章我們會介紹如何使用函數(shù)式接口及l(fā)ambda表達(dá)式進(jìn)行軟件的設(shè)計。
相關(guān)文章
Mybatis-Plus使用p6spy對SQL性能進(jìn)行監(jiān)控的方法
這篇文章主要介紹了Mybatis-Plus使用p6spy對SQL性能進(jìn)行監(jiān)控的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java實現(xiàn)將導(dǎo)出帶格式的Excel數(shù)據(jù)到Word表格
在Word中制作報表時,我們經(jīng)常需要將Excel中的數(shù)據(jù)復(fù)制粘貼到Word中,這樣則可以直接在Word文檔中查看數(shù)據(jù)而無需打開另一個Excel文件。本文將通過Java應(yīng)用程序詳細(xì)介紹如何把帶格式的Excel數(shù)據(jù)導(dǎo)入Word表格。希望這篇文章能對大家有所幫助2022-11-11聊聊Arrays.deepToString()和Arrays.toString()的區(qū)別
這篇文章主要介紹了聊聊Arrays.deepToString()和Arrays.toString()的區(qū)別,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02Spring Cloud Alibaba Nacos Config進(jìn)階使用
這篇文章主要介紹了Spring Cloud Alibaba Nacos Config進(jìn)階使用,文中使用企業(yè)案例,圖文并茂的展示了Nacos Config的使用,感興趣的小伙伴可以看一看2021-08-08Spring之兩種任務(wù)調(diào)度Scheduled和Async詳解
這篇文章主要介紹了Spring之兩種任務(wù)調(diào)度Scheduled和Async,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10springboot中shiro使用自定義注解屏蔽接口鑒權(quán)實現(xiàn)
本文主要介紹了springboot中shiro使用自定義注解屏蔽接口鑒權(quán)實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07springboot自定義redis-starter的實現(xiàn)
這篇文章主要介紹了springboot自定義redis-starter的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Java封裝公共Result結(jié)果返回類的實現(xiàn)
在使用Java開發(fā)接口請求中,我們需要對請求進(jìn)行進(jìn)行統(tǒng)一返回值,這時候我們自己封裝一個統(tǒng)一的Result返回類,本文主要介紹了Java封裝公共Result結(jié)果返回類的實現(xiàn),感興趣的可以了解一下2023-01-01