• 使用流式编程和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
  • 相关阅读:
    VINTF
    Excel 公式
    SSIS ODBC方式连接mysql数据库
    SSIS错误汇总
    linux防火墙(转)
    如何查询域名的MX、A、DNS、txt、cname记录
    IP反向解析
    Visual Studio 内存泄漏检测方法
    strcpy慎用
    main函数前后执行代码
  • 原文地址:https://www.cnblogs.com/null-/p/10016997.html
Copyright © 2020-2023  润新知