使用流式編程和lambda表達式實現java遍歷文件目錄


一、需求

  遍歷目錄是操作文件時的一個常見需求。比如寫一個程序,需要找到並處理指定目錄下的所有JS文件時,就需要遍歷整個目錄。該項目使用流式編程和lambda表達式,幫助你進一步熟悉java8特性,並且通過它實現目錄遍歷。

二、項目源代碼

 1 package com.wordcount.demo;
 2 
 3 import java.io.File;
 4 import java.nio.file.FileSystems;
 5 import java.nio.file.Files;
 6 import java.nio.file.Path;
 7 import java.util.Scanner;
 8 
 9 /**
10  * 遍歷目錄是操作文件時的一個常見需求。比如寫一個程序,需要找到並處理指定目錄下的所有JS文件時,
11  * 就需要遍歷整個目錄。該項目教會你如何使用流式編程和lambda表達式, 幫助你進一步熟悉java8特性,並且通過它實現目錄遍歷。
12  * 
13  * @author Administrator
14  */
15 public class DirFile {
16     public static void main(String[] args) throws Exception {
17         Scanner sc = new Scanner(System.in);
18         System.out.println("請輸入文件的絕對路徑:");
19         String path = sc.next();
20         // String EndName = sc.next();
21         oldMethod(path);
22         System.out.println("----------");
23         newMethod(path);
24     }
25     //傳統方法-用遞歸處理
26     public static void oldMethod(String path) {
27         if (new File(path).isDirectory()) {
28             String[] childs = new File(path).list();
29             for (String child : childs) {
30 
31                 oldMethod(path + "\\" + child);
32                 System.out.println(path + "\\" + child);
33             }
34         }
35     }
36     //利用Stream流式和lambda表達式完成
37     public static void newMethod(String path) throws Exception {
38         Path start = FileSystems.getDefault().getPath(path);
39         Files.walk(start).filter(childpath -> childpath.toFile().isFile())
40                 // .filter(path -> path.toString().endsWith(EndName))
41                 .forEach(System.out::println);
42     }
43 }

 

三、項目知識點學習

  1、File工具類基礎

    構造方法

public File(String pathname){}  
在pathname路徑下創建文件對象 public File(String path,String name){}
在path參數指定的目錄中創建具有給定名字的File對象,如果path為null,構造器將使用當前目錄創建對象 public File(File dir, String name){}
File對象dir表示一個目錄,在dir參數指定的目錄中創建具有給定名字的File對象,如果dir為null, 構造器將使用當前目錄創建對象

  File類的方法:http://www.runoob.com/java/java-file.html

  2、流式編程Stream?

  Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操作;高級版本的 Stream,用戶只要給出需要對其包含的元素執行什么操作,比如 “過濾掉長度大於 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。
  Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復返。而和迭代器又不同的是,Stream 可以並行化操作,迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完后再讀下一個 item。而使用並行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然后將結果一起輸出。Stream 的並行操作依賴於 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。

  Stream是用函數編程的方式在集合類上進行復雜操作的工具

  3、流的知識

  當我們使用一個流的時候,通常包括三個基本步驟:

  獲取一個數據源(source)→ 數據轉換→執行操作獲取想要的結果,每次轉換原有 Stream 對象不改變,返回一個新的 Stream 對象(可以有多次轉換),這就允許對其操作可以像鏈條一樣排列,變成一個管道,如下圖所示。

        

 

  有多種方式生成 Stream Source:

  從 Collection 和數組
    Collection.stream()
    Collection.parallelStream()
    Arrays.stream(T array) or Stream.of()
  從 BufferedReader
    java.io.BufferedReader.lines()
  靜態工廠
    java.util.stream.IntStream.range()
    java.nio.file.Files.walk()
  自己構建
    java.util.Spliterator
  其它
    Random.ints()
    BitSet.stream()
    Pattern.splitAsStream(java.lang.CharSequence)
    JarFile.stream()

 

  流的操作類型分為兩種:

  • Intermediate:一個流可以后面跟隨零個或多個 intermediate 操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然后返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。
  • Terminal:一個流只能有一個 terminal 操作,當這個操作執行后,流就被使用“光”了,無法再被操作。所以這必定是流的最后一個操作。Terminal 操作的執行,才會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。

在這里用的操作:

  filter

filter 對原始 Stream 進行某項測試,通過測試的元素被留下來生成一個新 Stream。

Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =
Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);

經過條件“被 2 整除”的 filter,剩下的數字為 {2, 4, 6}。(留下偶數

List<String> output = reader.lines().
 flatMap(line -> Stream.of(line.split(REGEXP))).
 filter(word -> word.length() > 0).
 collect(Collectors.toList());

這段代碼首先把每行的單詞用 flatMap 整理到新的 Stream,然后保留長度不為 0 的,就是整篇文章中的全部單詞了。(把單詞挑出來

forEach

forEach 方法接收一個 Lambda 表達式,然后在 Stream 的每一個元素上執行該表達式。

// Java 8
roster.stream()
 .filter(p -> p.getGender() == Person.Sex.MALE)
 .forEach(p -> System.out.println(p.getName()));
// Pre-Java 8
for (Person p : roster) {
 if (p.getGender() == Person.Sex.MALE) {
 System.out.println(p.getName());
 }
}

對一個人員集合遍歷,找出男性並打印姓名。可以看出來,forEach 是為 Lambda 而設計的,保持了最緊湊的風格。而且 Lambda 表達式本身是可以重用的,非常方便。當需要為多核系統優化時,可以 parallelStream().forEach(),只是此時原有元素的次序沒法保證,並行的情況下將改變串行時操作的行為,此時 forEach 本身的實現不需要調整,而 Java8 以前的 for 循環 code 可能需要加入額外的多線程邏輯。(打印姓名

 

總之,Stream 的特性可以歸納為:

  • 不是數據結構
  • 它沒有內部存儲,它只是用操作管道從 source(數據結構、數組、generator function、IO channel)抓取數據。
  • 它也絕不修改自己所封裝的底層數據結構的數據。例如 Stream 的 filter 操作會產生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素。
  • 所有 Stream 的操作必須以 lambda 表達式為參數
  • 不支持索引訪問
  • 你可以請求第一個元素,但無法請求第二個,第三個,或最后一個。不過請參閱下一項。
  • 很容易生成數組或者 List
  • 惰性化
  • 很多 Stream 操作是向后延遲的,一直到它弄清楚了最后需要多少數據才會開始。
  • Intermediate 操作永遠是惰性化的。
  • 並行能力
  • 當一個 Stream 是並行化的,就不需要再寫多線程代碼,所有對它的操作會自動並行進行的。
  • 可以是無限的
    • 集合有固定大小,Stream 則不必。limit(n) 和 findFirst() 這類的 short-circuiting 操作可以對無限的 Stream 進行運算並很快完成。   

  

  4、lambda表達式

  Lambda 表達式,也可稱為閉包,它是推動 Java 8 發布的最重要新特性。
  Lambda 允許把函數作為一個方法的參數(函數作為參數傳遞進方法中)。
  使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。

以下是lambda表達式的重要特征:

  • 可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
  • 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
  • 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
  • 可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。

使用 Lambda 表達式需要注意以下兩點:

  • Lambda 表達式主要用來定義行內執行的方法類型接口,例如,一個簡單方法接口。在上面例子中,我們使用各種類型的Lambda表達式來定義MathOperation接口的方法。然后我們定義了sayMessage的執行。
  • Lambda 表達式免去了使用匿名方法的麻煩,並且給予Java簡單但是強大的函數化的編程能力。

lambda表達式示例:

 1 public class Java8Tester {
 2    public static void main(String args[]){
 3       Java8Tester tester = new Java8Tester();
 4         
 5       // 類型聲明
 6       MathOperation addition = (int a, int b) -> a + b;
 7         
 8       // 不用類型聲明
 9       MathOperation subtraction = (a, b) -> a - b;
10         
11       // 大括號中的返回語句
12       MathOperation multiplication = (int a, int b) -> { return a * b; };
13         
14       // 沒有大括號及返回語句
15       MathOperation division = (int a, int b) -> a / b;
16         
17       System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
18       System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
19       System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
20       System.out.println("10 / 5 = " + tester.operate(10, 5, division));
21         
22       // 不用括號
23       GreetingService greetService1 = message ->
24       System.out.println("Hello " + message);
25         
26       // 用括號
27       GreetingService greetService2 = (message) ->
28       System.out.println("Hello " + message);
29         
30       greetService1.sayMessage("Runoob");
31       greetService2.sayMessage("Google");
32    }
33     
34    interface MathOperation {
35       int operation(int a, int b);
36    }
37     
38    interface GreetingService {
39       void sayMessage(String message);
40    }
41     
42    private int operate(int a, int b, MathOperation mathOperation){
43       return mathOperation.operation(a, b);
44    }
45 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM