【java学习笔记】java se8的流库概念以及使用


概念

流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。我们将操作的调度留给具体实现去解决。例如,假设我们想要计算某个属性的平均值,那么我们就可以指定数据源和该属性,然后,流库就可以对计算进行优化,例如,使用多线程来计算总和与个数,并将结果合并。它是在Java SE 8中引入的,用来以“做什么而非怎么做”的方式处理集合。

代码实例

下面一个例子:

   String contents = new String(Files,readAllBytes(Paths.get("mm.txt"), StandardCharsets.UTF_8));
        List<String>words = Arrays.asList(contents.split("\\PL+"));
        //long count = 0;
        for(String w:words)
        {
            if(w.length()>12)count++;
        }
        //如果使用流,也可以用这样的写法
        long count = words.stream().filter(w->w.length()>12).count();
 Stream<T> filter(Predicate<? super T> p) //产生一个流,其中包含当前流中满足P的所有元素。`

·long count() 产生当前流中元素的数量。这是一个终止操作。

java.util.Collection<E>1.2

·default Stream<E> stream()
·default Stream<E> parallelStream()
产生当前集合中所有元素的顺序流或并行流。
流表a面上看起来和集合很类似,都可以让我们转换和获取数据。但是,它们之间存在着显著的差异:
1.流并不存储其元素。这些元素可能存储在底层的集合中,或者是按需生成的。
2.流的操作不会修改其数据源。例如,filter方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
3.流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才 会执行。例如,如果我们只想查找前5个长单词而不是所有长单词,那么filter方法就会在匹配到第5个单词后停止过滤。因此,我们甚至可以操作无限流。

操作流时的典型流程:建立一个包含三个阶段的操作管道:
1.创建一个流。 2.指定将初始流转换为其他流的中间操作,可能包含多个步骤。 3.应用终止操作,从而产生结果。这个操作会强制执行之前的惰性操作。从此之后,这个流就再也不能用了。也可以用parallelStream方法创建并行流

流的创建

可以用Collection接口的stream方法将任何集合转换为一个流。如果你有一个数组,那么可以使用静态的Stream.of方法。

Stream<String>words = Stream.of(contends.split("\\pl+");//split returns a String[] array

of方法具有可变长参数,因此我们可以构建具有任意数量引元的流:

Stream<String>words = Stream.of("my","you","and");

使用Array.stream(array,from,to)可以从数组中位于from(包括)和to(不包括)的元素中创建一个流
为了创建不包含任何元素的流,可以使用静态的Stream.empty方法:

Stream<String>silence = Stream.empty();

Stream接口有两个用于创建无限流的静态方法。generate方法会接受一个不包含任何引元的函数(或者从技术上讲,是一个Supplier<T>接口的对象)

Stream<String>echos = Stream.generate(()->"Echo");

static <T> Stream<T> generate(Supplier<T> s)

Where, Stream is an interface and T is the type of stream elements. s
is the Supplier of generated elements and the return value is a new
infinite sequential unordered Stream.

我们可以像下面这样获得一个常量值的流: 或者像下面这样获取一个随机数的流:

 public static void main(String[] args) {
        Stream.generate(new Random()::nextInt)
                .limit(5).forEach(System.out::println);
    }

输出
-1891038009
-1978949720
107075108
196723573
2041949678

惰性求值与及早求值

惰性求值:只描述Stream,操作的结果也是Stream,这样的操作称为惰性求值。惰性求值可以像建造者模式一样链式使用,最后再使用及早求值得到最终结果。
及早求值:得到最终的结果而不是Stream,这样的操作称为及早求值。

collect(Collectors.toList())
//将流转换为list。还有toSet(),toMap()等。及早求值。
public class TestCase {
    public static void main(String[] args) {
        List<Student> studentList = Stream.of(new Student("路飞", 22, 175),
                new Student("红发", 40, 180),
                new Student("白胡子", 50, 185)).collect(Collectors.toList());
        System.out.println(studentList);
    }
} 

filter的引元是Predicate/,即从T到boolean的函数。 通常,我们想要按照某种方式来转换流中的值,此时,可以使用map方法并传递执行该转换的函数
map
转换功能,内部就是Function接口。惰性求值。例如,

//惰性求值
public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        List<String> names = students.stream().map(student -> student.getName())
                .collect(Collectors.toList());
        System.out.println(names);
    }
}
//输出结果
//[路飞, 红发, 白胡子] 

流的转换会产生一个新的流,它的元素派生自另一个流中的元素。我们已经看到了filter转换会产生一个流,它的元素与某种条件相匹配。如上面的一个代码,我们将一个字符串流转换为了只包含长单词的另一个流
我们可以像下面这样将所有单词都转换为小写:

Stream<String>lowercaseWords = words.stream().map(String::toLowerCase);

这里,我们使用的是带有方法引用的map,但是,通常我们可以使用lambda表达式来代替:

Stream<String>firstLetters = words.stream().map(s->s.substring(0,1)
//产生的流中包含了所有单词的首字母

·Stream<T>filter(Predicate<?super T>predicate)
产生一个流,它包含当前流中所有满足断言条件的元素。 ·<R>Stream<R>map(Function<?super
T,?extends R>mapper) 产生一个流,它包含将mapper应用于当前流中所有元素所产生的结果。
·<R>Stream<R>flatMap(Function<?super T,?extends Stream<?extends
R>>mapper)
产生一个流,它是通过将mapper应用于当前流中所有元素所产生的结果连接到一起而获得的。(注意,这里的每个结果都是一个流。)

flatmap
多个stream合并为一个stream

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        List<Student> studentList = Stream.of(students,
                asList(new Student("艾斯", 25, 183),
                        new Student("雷利", 48, 176)))
                .flatMap(students1 -> students1.stream()).collect(Collectors.toList());
        System.out.println(studentList);
    } 
    //结果将五个人都输出

调用Stream.of的静态方法将两个list转换为Stream,再通过flatMap将两个流合并为一个。
max和min
我们经常会在集合中求最大或最小值,使用流就很方便。及早求值。

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        Optional<Student> max = students.stream()
            .max(Comparator.comparing(stu -> stu.getAge()));
        Optional<Student> min = students.stream()
            .min(Comparator.comparing(stu -> stu.getAge()));
        //判断是否有值
        if (max.isPresent()) {
            System.out.println(max.get());
        }
        if (min.isPresent()) {
            System.out.println(min.get());
        }
    }
}
//输出结果
//Student{name='白胡子', age=50, stature=185, specialities=null}
//Student{name='路飞', age=22, stature=175, specialities=null}

max、min接收一个Comparator(例子中使用java8自带的静态函数,只需要传进需要比较值即可。)并且返回一个Optional对象,该对象是java8新增的类,专门为了防止null引发的空指针异常。

reduce
reduce 操作可以实现从一组值中生成一个值。在上述例子中用到的 count 、 min 和 max 方法,因为常用而被纳入标准库中。事实上,这些方法都是 reduce 操作。及早求值。

    public static void main(String[] args) {
    Integer integer = Stream.of(1,2,3,4,5).reduce(0,(acc,x)->acc+x);
        System.out.println(integer);
    }

输出15

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值