欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA 8的Lambda表达式和Stream API研究 博客分类: Java编程  

程序员文章站 2024-03-19 09:59:40
...

JAVA 8 Lambda表达式简化了代码开发,代码简洁,类似Python。

代码实现例子:

 

new Thread(() -> System.out.println("It's a lambda function!")).start();

 

List<String> languagesList = Arrays.asList("java","scala","python");

languagesList.forEach(x -> System.out.println(x));

languagesList.forEach(System.out::println);

 

 

List<Double> costList = Arrays.asList(10.0, 20.0,30.0);

costList.stream().map(x -> x + x*0.05).forEach(x -> System.out.println(x));

 

 

 

List<Double> costList = Arrays.asList(10.0, 20.0,30.0);

double allCost = costList.stream().map(x -> x+x*0.05).reduce((sum,x) -> sum + x).get();

System.out.println(allCost);

 

 

 

List<Double> costList = Arrays.asList(10.0, 20.0,30.0,40.0);

List<Double> filteredCostList = costList.stream().filter(x -> x > 25.0).collect(Collectors.toList());

filteredCostList.forEach(x -> System.out.println(x));

 

 

 

List<String> languagesList = Arrays.asList("Java","Python","scala","Shell","R");

filterTest(languagesList,x -> x.startsWith("J"));

 

public static void filterTest(List<String> languagesList, Predicate<String> condition) {

  languagesList.stream().filter(x -> condition.test(x)).forEach(x -> System.out.println(x + " "));

}

 

============================================================================

 

通过使用Stream API对集合数据进行操作,就类似于使用 SQL 执行的数据库查询,也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

 

 

List<Integer> list = Arrays.asList(2, 3, 5, 4, 1, 8, 10, 9, 7, 6);

list.stream().filter((e) -> e > 5).forEach(System.out::println);

 

 

使用Stream操作有三个步骤

 

创建 Stream:从一个数据源(如集合、数组)中获取一个流

中间操作:一个或多个中间操作,对数据源的数据进行处理

终止操作:执行中间操作链,并产生结果

 

Java8 中的 Collection 接口被扩展了,提供了两个获取流的方法:

 

default Stream stream() : 返回一个顺序流

default Stream parallelStream() : 返回一个并行流

 

List<String> list = new ArrayList<>();

Stream<String> stream = list.stream(); //获取一个顺序流

Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

 

 

Java8 中的 Arrays 的静态方法 stream() 可以从数组获取流

Integer[] nums = new Integer[8];

Stream<Integer> stream = Arrays.stream(nums);

 

 

由值创建流

 

可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数

Stream<Integer> stream = Stream.of(1,2,3,4,5);

 

由函数创建流

可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。

Stream.iterate(1, (x) -> x + 1).forEach(System.out::println);

Stream.generate(() -> Math.random()).forEach(System.out::println);

 

中间操作又分四类

 

筛选

切割

映射

排序

 

filter(Predicate p), 接收Predicate实例,根据该实例的test方法进行筛选,如

 

List<Integer> list = Arrays.asList(2, 3, 5, 4, 1, 8, 10, 9, 7, 6);

    list.stream()

            .filter((e) -> e > 5)

            .forEach(System.out::println);

 

distinct(),这个方法大家应该能看出来就是去重复的,根据流所生成元素的 hashCode() 和 equals() 去除重复元素

 

List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 4, 5, 6);

    list.stream()

            .distinct()

            .forEach(System.out::println);

 

 

切割

 

切割也有两个方法

 

limit(long maxSize),截断流,使其元素不超过给定数量,跟SQL语句很像

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);

    list.stream()

            .limit(3)

            .forEach(System.out::println);

 

skip(long n) ,返回一个去掉了前 n 个元素的流。若流中元素 

不足 n 个,则返回一个空流。

 

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);

    list.stream()

            .skip(3)

            .forEach(System.out::println);

 

可以看到这个方法刚好跟limit方法互补了,limit去尾,skip去头。

 

映射

 

map(Function f) ,接收一个Function实例,Function的抽象方法apply是接收一个参数然后返回一个值,这个返回值就是映射的结果

 

List<Integer> list = Arrays.asList(1, 2, 3);

    list.stream()

            .map(x -> x*x)

            .forEach(System.out::println);

 

mapToDouble(ToDoubleFunction f),这个函数跟map很像,只不过映射的结果一定是要Double类型

List<Integer> list = Arrays.asList(1, 2, 3);

    list.stream()

            .mapToDouble(x -> x+0.1)

            .forEach(System.out::println);

 

可以发现Integer类型的元素被映射成Double类型了

类似的还有mapToInt(ToIntFunction f)和mapToLong(ToLongFunction f)方法,这里就不去演示了。

 

List<String> list = Arrays.asList("abc", "efg", "xyz");

        list.stream()//

                .flatMap(TestStreamAPI::string2Stream)//

                .forEach(System.out::println);

 

/**

     * 接收一个字符串将其所以的字符添加到list中然后返回stream

     * @param str

     * @return

     */

    public static Stream<Character> string2Stream(String str) {

        List<Character> list = new ArrayList<>();

        char[] charArray = str.toCharArray();

        for (char c : charArray) {

            list.add(c);

        }

        return list.stream();

    }

 

其实这也不难理解,就像上面mapToDouble方法把流中的每个元素映射成一个Double类型的值一样,现在只是映射成一个Stream

 

 

排序

 

sorted(),可以将流中元素按自然顺序排序。

List<String> list = Arrays.asList("d", "a", "c");

    list.stream()

            .sorted()

            .forEach(System.out::println);

 

sorted(Comparator comp),可以将流中元素按比较器顺序排序。

List<String> list = Arrays.asList("d", "a", "c");

    list.stream()

            .sorted((x,y) -> -x.compareTo(y))

            .forEach(System.out::println);

 

终止操作

终止操作会从流的中间操作流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer,甚至是 void 。

 

其中终止操作又有三类

 

查找与匹配

归约

收集

 

查找与匹配

 

查找与匹配有下面这么多方法,我只演示几个典型的。

 

allMatch(Predicate p) 检查是否匹配所有元素

anyMatch(Predicate p) 检查是否至少匹配一个元素

noneMatch(Predicate p) 检查是否没有匹配所有元素

findFirst() 返回第一个元素

findAny() 返回当前流中的任意元素

count() 返回流中元素总数

max(Comparator c) 返回流中最大值

min(Comparator c) 返回流中最小值

forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反, Stream API 使用内部迭代)

 

allMatch(Predicate p)例子

List<Integer> list = Arrays.asList(10, 5, 7, 3);

    boolean allMatch = list.stream()//

            .allMatch(x -> x > 2);//是否全部元素都大于2

    System.out.println(allMatch);

 

findFirst()例子

List<Integer> list = Arrays.asList(10, 5, 7, 3);

    Optional<Integer> first = list.stream()//

            .findFirst();

    Integer val = first.get();

    System.out.println(val);//输出10

 

max(Comparator c)例子

List<Integer> list = Arrays.asList(10, 5, 7, 3);

    Optional<Integer> first = list.stream()//

            .min(Integer::compareTo);

    Integer val = first.get();

    System.out.println(val);//输出3

 

归约

 

归约操作有下面两个方法

 

reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T

reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional

 

reduce(T iden, BinaryOperator b)例子

 

List<Integer> list = Arrays.asList(10, 5, 7, 3);

    Integer result = list.stream()

        .reduce(2, Integer::sum);

    System.out.println(result);//输出27,其实相当于2+10+5+7+3,就是一个累加

 

reduce(BinaryOperator b)例子

List<Integer> list = Arrays.asList(10, 5, 7, 3);

    Optional<Integer> optional = list.stream()

        .reduce(Integer::sum);

    Integer result = optional.get();

    System.out.println(result);//输出25,其实相当于10+5+7+3,就是一个累加

 

收集

 

collect(Collector c) 将流转换为其他形式。接收一个Collector接口的 

实现,用于给流中元素做汇总的方法

 

List<Integer> list = Arrays.asList(10, 5, 7, 3);

    // 将流中元素收集到List中

    List<Integer> resultList = list.stream()

            .collect(Collectors.toList());

 

    // 将流中元素收集到Set中

    Set<Integer> resultSet = list.stream()

            .collect(Collectors.toSet());

 

    System.out.println(resultList);// 输出[10, 5, 7, 3]

    System.out.println(resultSet);// 输出[3, 5, 7, 10]

 

上面的代码分别将流中的元素收集到List和Set中,其实还可以收集到其类型中,如Map、Optional甚至是Integer等。在收集过程中还可以做点其它操作,比如下面例子,至于其它更多的操作大家还是去看API吧

 

List<Integer> list = Arrays.asList(10, 5, 7, 3);

 

    //计算出流中的元素个数

    Long count = list.stream()//

            .collect(Collectors.counting());

    System.out.println(count);//输出4

 

    //计算流中Integer类型数据的总和

    Integer sum = list.stream()//

            .collect(Collectors.summingInt(x -> x));

    System.out.println(sum);//输出25

  

 

Stream延迟执行

上面“中间操作”那节我说到了“惰性求值”这个东西,即Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

 

 List<Person> list = Arrays.asList(//

            new Person("Jason", 18), //

            new Person("Hank", 46), //

            new Person("Alice", 23));

    // 过滤操作,找出age大于20的Person对象,但是没有终止操作

    list.stream()//

            .filter(x -> x.getAge() > 20);

运行这个测试方法,你会发现没有输出任何东西,因为它并没有终止操作,所以中间操作并未执行。下面加多一个终止操作

    List<Person> list = Arrays.asList(//

            new Person("Jason", 18), //

            new Person("Hank", 46), //

            new Person("Alice", 23));

    // 过滤操作,找出age大于20的Person对象,但是没有终止操作

    list.stream()//

            .filter(x -> x.getAge() > 20)//

            .forEach(System.out::println);

 

 

Stream API还提供了并行流,即多线程执行操作。

 

java.util.Collection<E>新添加了两个默认方法

 

default Stream stream() : 返回串行流

default Stream parallelStream() : 返回并行流

可以发现,stream()和parallelStream()方法返回的都是java.util.stream.Stream<E>类型的对象,说明它们在功能的使用上是没差别的。唯一的差别就是单线程和多线程的执行,关于Stream API还不了解的可以去我开头给出的链接那文章看看。下面我就使用一下并行流,对比串行流看看性能上的差距。

 

 

List<Integer> list = new ArrayList<>();

        // 将10000-1存入list中

        for (int i = 10000; i >= 1; i--) {

            list.add(i);

        }

 

list.stream()// 获取串行流

                .sorted()// 按自然排序,即按数字从小到大排序

                .count();// count()是终止操作,有终止操作才会执行中间操作sorted()

 

List<Integer> list = new ArrayList<>();

        // 将10000-1存入list中

        for (int i = 10000; i >= 1; i--) {

            list.add(i);

        }

 

list.parallelStream()// 获取并行流

                .sorted()// 按自然排序,即按数字从小到大排序

                .count();// count()是终止操作,有终止操作才会执行中间操作sorted()

 

from:

http://blog.csdn.net/timheath/article/details/71366448

http://blog.csdn.net/timheath/article/details/71275179