Stream流——Java8新特性之一

用于处理集合,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

Java Steam的操作是基于集合的。Steam的操作可以分为两种:中间操作和结束操作。Stream操作是延迟执行的。他会等到有结束操作的时候才执行中间操作链。

网上一般说它有三个特点:

  • 不是数据结构,不会保存数据。

  • 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(但是peek和map其实都能改变源数据)

  • 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。

下面开始讲解:
使用到的实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {private String name;private int age;
}

@Data、@AllArgsConstructor、@NoArgsConstructor三个注解是在maven里面引用了lombok,作用是帮我们创建了get、set、有参与无参构造方法,也可以自己手动创建。

流的创建:

// 1、通过 Collection.stream() 方法用集合创建流
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
Stream<String> parallelStream2 = list.stream().parallel();// 2、使用 Arrays.stream(T[] array) 方法用数组创建流
int[] array={1,3,5,6,8};
IntStream intStream = Arrays.stream(array);// 3、使用Stream的静态方法:of()、iterate()、generate()
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);
stream1.forEach(System.out::println);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(10);
stream2.forEach(System.out::println);
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

filter:筛选

List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
Stream<Integer> stream = list.stream();
stream.filter(x -> x > 7).forEach(System.out::println);

concat:合并

String[] arr1 = {"a", "b", "c", "d"};
String[] arr2 = {"d", "e", "f", "g"};
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
List<String> newList = Stream.concat(stream1, stream2).collect(Collectors.toList());
System.out.println("流合并:" + newList);// 流合并:[a, b, c, d, d, e, f, g]

distinct:去重

Integer[] arr = {1, 2, 2, 3, 4, 4, 5};
List<Integer> list = Arrays.stream(arr).distinct().collect(Collectors.toList());
System.out.println("流去重:" + list);// 流去重:[1, 2, 3, 4, 5]

skip:跳过前n个数据

List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("skip:" + collect2);// skip:[3, 5, 7, 9, 11]

limit:获取前n个元素

List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
System.out.println("limit:" + collect);// limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

skip和limit加起来可以实现分页功能。

map:映射,函数会被应用到每个元素上,并将其映射成一个新的元素。

String[] strArr = {"rety", "fghfg", "terd", "nye"};
List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
System.out.println("每个元素大写:" + strList);// 每个元素大写:[RETY, FGHFG, TERD, NYE]//将每个元素转成一个新的且不带逗号的元素
List<String> list = Arrays.asList("h,y,g,g,e", "3,6,9,12");
List<String> listNew = list.stream().map(s -> s.replaceAll(",", "")).collect(Collectors.toList());
System.out.println("去掉逗号:" + listNew);// 去掉逗号:[hygge, 36912]List<Integer> intList = Arrays.asList(2, 4, 5, 8, 10);
List<Integer> intListNew = intList.stream().map(x -> x + 2).collect(Collectors.toList());
System.out.println("每个元素+3:" + intListNew);// 每个元素+3:[4, 6, 7, 10, 12]

flatMap:映射,流中的每个值都换成另一个流,然后把所有流连接成一个流。

List<String> list = Arrays.asList("h,y,g,g,e", "3,6,9,12");
List<String> listNew = list.stream().flatMap(s -> {//将每个元素转换成一个streamString[] split = s.split(",");Stream<String> s2 = Arrays.stream(split);return s2;
}).collect(Collectors.toList());
System.out.println("处理前:元素个数为 " + list.size() + " 个,集合为 " + list);
System.out.println("处理后:元素个数为 " + listNew.size() + " 个,集合为 " + listNew);/*
*  处理前:元素个数为 2 个,集合为 [h,y,g,g,e, 3,6,9,12]
*  处理后:元素个数为 9 个,集合为 [h, y, g, g, e, 3, 6, 9, 12]
*/

这里注意虽然他们的集合打印出来很相似,但是注意他们的元素个数是不一样的。

sorted:排序

// 默认排序:即无参时
List<Integer> list = Arrays.asList(5, 6, 4);
list.stream().sorted().forEach(System.out::println);
// 自定义排序
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("A", 10));
studentList.add(new Student("B", 20));
studentList.add(new Student("A", 30));
studentList.add(new Student("D", 40));
// 按年龄升序
studentList.stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);
// 先按姓名升序,姓名相同则按年龄升序
studentList.stream().sorted((o1, o2) -> {if (o1.getName().equals(o2.getName())) {return o1.getAge() - o2.getAge();} else {return o1.getName().compareTo(o2.getName());}}
).forEach(System.out::println);
// 先按姓名升序,姓名相同则按年龄升序,更简单的写法
studentList.stream().sorted(Comparator.comparing(Student::getName).thenComparing(Student::getAge)).forEach(System.out::println);

peek:消费

List<Student> studentList = new ArrayList<>();
studentList.add(new Student("A", 10));
studentList.add(new Student("B", 20));
System.out.println("原始数据:" + studentList);// 使用map对比
List<Student> result = studentList.stream().map(o -> {o.setAge(50);return o;
}).collect(Collectors.toList());
System.out.println("使用map修改数据:" + result);// 使用peek
List<Student> result1 = studentList.stream().peek(o -> o.setAge(100)).collect(Collectors.toList());
System.out.println("使用peek修改数据:" + result1);/*
* 原始数据:[Student(name=A, age=10), Student(name=B, age=20)]
* 使用map修改数据:[Student(name=A, age=50), Student(name=B, age=50)]
* 使用peek修改数据:[Student(name=A, age=100), Student(name=B, age=100)]
*/

这里专门使用了map与peek做了个对比,你会发现他们都改变了age的值,而peek区别与map,它不需要返回值。但是map能改变数据类型,peek就做不到。

所以peek 可以做一些打印或者修改工作。

源码注释中作者给的例子也证明了这种想法:

Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("Filtered value: " + e)).map(String::toUpperCase).peek(e -> System.out.println("Mapped value: " + e)).collect(Collectors.toList());

对于map与peek的具体分析看这篇文章:——(待整理)——

max:返回流中元素的最大值

List<String> list = Arrays.asList("admn", "bgfmt", "pbtd", "xbafdd", "weoufgsd");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:" + max.get());// 最长的字符串:weoufgsdList<Integer> intlist = Arrays.asList(7, 6, 9, 4, 11, 6);
Optional<Integer> max2 = intlist.stream().max(Integer::compareTo);
System.out.println("自然排序的最大值:" + max2.get());// 自然排序的最大值:11

min:返回流中元素的最小值

List<String> list = Arrays.asList("admn", "bgfmt", "pbtd", "xbafdd", "weoufgsd");
Optional<String> min = list.stream().min(Comparator.comparing(String::length));
System.out.println("最短的字符串:" + min.get());// 最短的字符串:admnList<Integer> intList = Arrays.asList(7, 6, 9, 4, 11, 6);
Optional<Integer> min2 = intList.stream().min(Integer::compareTo);
System.out.println("自然排序的最小值:" + min2.get());// 自然排序的最小值:4

count:返回流中元素的总个数

List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
long count = list.stream().count();
System.out.println("list的元素总个数:" + count);// list的元素总个数:7long countOfGt6 = list.stream().filter(x -> x > 6).count();
System.out.println("list中大于6的元素个数:" + countOfGt6);// list中大于6的元素个数:4

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
boolean allMatch = list.stream().allMatch(x -> x > 6);
System.out.println("是否全部值都大于6:" + allMatch);// 是否全部值都大于6:false

noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
boolean noneMatch = list.stream().noneMatch(x -> x > 6);
System.out.println("是否不存在大于6的值:" + noneMatch);// 是否不存在大于6的值:false

anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
boolean anyMatch = list.stream().anyMatch(x -> x > 6);
System.out.println("是否存在大于6的值:" + anyMatch);// 是否存在大于6的值:true

findFirst:返回流中第一个元素

List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
Optional<Integer> findFirst = list.stream().findFirst();
System.out.println("list中的第一个数:"+findFirst.get());// list中的第一个数:7Optional<Integer> findFirstGt7 = list.stream().filter(x -> x > 7).findFirst();
System.out.println("list中第一个大于7的数:"+findFirstGt7.get());// list中第一个大于7的数:9

findAny:返回流中的任意元素

// 适用于并行流,源码有注释:This is to allow for maximal performance in parallel operations
List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
Optional<Integer> findAny = list.parallelStream().findAny();
System.out.println("匹配任意一个值:" + findAny.get());// 匹配任意一个值:8Optional<Integer> findAnyGt5 = list.parallelStream().filter(x -> x > 5).findAny();
System.out.println("匹配任意一个大于5的值:" + findAnyGt5.get());// 匹配任意一个大于5的值:8

PS:使用上面代码自己测试的时候,得多次运行才可能得到不一样的返回值,可以试着改变list的长度或者类型来增加返回不同值的概率。

reduce:归约

例子中也可以使用(x, y) -> x > y ? x : y 替换 Integer::max

List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);// 一个参数:Optional<T> reduce(BinaryOperator<T> accumulator)
Optional<Integer> sum = list.stream().reduce(Integer::sum);
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
Optional<Integer> max = list.stream().reduce(Integer::max);
System.out.println("list求和:" + sum.get()+ ",求积:" + product.get()+",最大值:" + max.get());// list求和:29,求积:2112,最大值:11// 两个参数:T reduce(T identity, BinaryOperator<T> accumulator)
Integer sum2 = list.stream().reduce(0, Integer::sum);
Integer product2 = list.stream().reduce(1, (x, y) -> x * y);
Integer max2 = list.stream().reduce(0, Integer::max);
System.out.println("list求和:" + sum2+ ",求积:" + product2+",最大值:" + max2);// list求和:29,求积:2112,最大值:11// <U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner)
Integer sum3 = list.parallelStream().reduce(0, Integer::sum, Integer::sum);
Integer product3 = list.stream().reduce(1, (x, y) -> x * y, (x, y) -> x * y);
Integer max3 = list.stream().reduce(0, Integer::max, Integer::max);
System.out.println("list求和:" + sum3+ ",求积:" + product3+",最大值:" + max3);// list求和:29,求积:2112,最大值:11

reduce三种不同方式的分析,请看这篇文章:——(待整理)——

collect:归集 (Collectors:toList/toSet/toMap)

List<Student> students = new ArrayList<>();
students.add(new Student("A", 10));
students.add(new Student("B", 20));
students.add(new Student("C", 10));
// 转成list:toList
List<Integer> ageList = students.stream().map(Student::getAge).collect(Collectors.toList());
System.out.println("ageList:" + ageList);// ageList:[10, 20, 10]
// 转成Set:toSet
Set<Integer> ageSet = students.stream().map(Student::getAge).collect(Collectors.toSet());
System.out.println("ageSet:" + ageSet);// ageSet:[20, 10]
// 类似Set:distinct去重
List<Integer> ageSet1 =  students.stream().map(Student::getAge).distinct().collect(Collectors.toList());
System.out.println("ageSet1:" + ageSet1);// ageSet1:[10, 20]
// 转成Map:toMap
Map<String, Integer> studentMap = students.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
System.out.println("studentMap:" + studentMap);// studentMap:{A=10, B=20, C=10}

使用collect(Collectors.toSet())与distinct().collect(Collectors.toList()),最终都能得到一个包含不重复元素的集合,但是你会发现他们元素的顺序不一样,可以大胆的猜测他们写入的顺序不一样,具体分析请看文章:——(待整理)——

上面基本上列举出了常用的方法,面对实际问题,我们使用Stream时,可以采用非常多的方式来达到目的。下面我们用一个例子以及一些问题,尽可能的给出多种解决方案,来让大家来熟悉这些方法。

用到的实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {private String name;  // 姓名private int salary;   // 薪资private int age;      // 年龄private String sex;   // 性别private String area;  // 地区
}

实例化:

List<Person> personList = new ArrayList<Person>();
personList.add(new Person("张三", 39000, 23, "男", "北京"));
personList.add(new Person("王五", 20000, 25, "男", "上海"));
personList.add(new Person("李四", 28000, 21, "女", "上海"));
personList.add(new Person("小明", 32000, 24, "女", "北京"));
personList.add(new Person("小刚", 45000, 25, "男", "北京"));
personList.add(new Person("小红", 29000, 26, "女", "上海"));

1、工资大于28000的人员列表

Map<String, Integer> map = personList.stream().filter(p -> p.getSalary() > 28000).collect(Collectors.toMap(Person::getName, Person::getSalary));
System.out.println("高于28000的员工姓名:" + map.keySet());List<String> List = personList.stream().filter(x -> x.getSalary() > 28000).map(Person::getName).collect(Collectors.toList());
System.out.println("高于28000的员工姓名:" + List);/*
* 高于28000的员工姓名:[小刚, 张三, 小明, 小红]
* 高于28000的员工姓名:[张三, 小明, 小刚, 小红]
*/

2、改变员工信息

// 不改变原来员工集合的方式
List<Person> personListNew = personList.stream().map(person -> {Person personNew = new Person(person.getName(), 0, 0, null, null);personNew.setSalary(person.getSalary() + 10000);return personNew;
}).collect(Collectors.toList());
System.out.println("一次改动前:" + personList);
System.out.println("一次改动后:" + personListNew);// 改变原来员工集合的方式
List<Person> personListNew2 = personList.stream().map(person -> {person.setSalary(person.getSalary() + 10000);return person;
}).collect(Collectors.toList());
System.out.println("二次改动前:" + personList);
System.out.println("二次改动后:" + personListNew2);

3、求工资之和方式:

Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);Integer sumSalary2 = personList.stream().map(Person::getSalary).reduce(0, Integer::sum);Integer sumSalary3 = personList.parallelStream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);OptionalInt sumSalary4 = personList.stream().mapToInt(Person::getSalary).reduce(Integer::sum);int sumSalary5 = personList.stream().mapToInt(Person::getSalary).sum();Integer sumSalary6 = personList.stream().collect(Collectors.summingInt(Person::getSalary));System.out.println("工资之和:" + sumSalary.get() + "," + sumSalary2 + "," + sumSalary3 + "," +sumSalary4.getAsInt() + "," + sumSalary5 + "," + sumSalary6);/*
* 工资之和:193000,193000,193000,193000,193000,193000
*/

4、求最高工资方式:

Optional<Integer> maxSalary = personList.stream().map(Person::getSalary).reduce((max1, max2) -> max1 > max2 ? max1 : max2);Optional<Integer> maxSalary2 = personList.stream().map(Person::getSalary).reduce(Integer::max);Integer maxSalary3 = personList.stream().map(Person::getSalary).reduce(0, Integer::max);Integer maxSalary4 = personList.parallelStream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(), Integer::max);Optional<Integer> maxSalary5 = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));Optional<Integer> maxSalary6 = personList.stream().map(Person::getSalary).max(Integer::compare);Optional<Person> maxSalary7 = personList.stream().max(Comparator.comparingInt(Person::getSalary));System.out.println("最高工资:" + maxSalary.get() + "," + maxSalary2.get() + "," + maxSalary3 + ","+ maxSalary4 + "," + maxSalary5.get() + "," + maxSalary6.get() + "," + maxSalary7.get().getSalary());/*
* 最高工资:45000,45000,45000,45000,45000,45000,45000
*/

5、求平均工资

Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));Double average2 = personList.stream().mapToDouble(Person::getSalary).sum() / personList.size();System.out.println("员工平均工资:" + average + "," + average2);/*
* 员工平均工资:32166.666666666668,32166.666666666668
*/

6、求人员总数

long count = personList.stream().collect(Collectors.counting());long count2 = personList.stream().count();long count3 = personList.size();System.out.println("员工总数:" + count + "," + count2 + "," + count3);/*
* 员工总数:6,6,6
*/

7、一次性统计所有信息

DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println("员工工资所有统计:" + collect);/*
* 员工工资所有统计:DoubleSummaryStatistics{count=6, sum=193000.000000, min=20000.000000, average=32166.666667, max=45000.000000}
*/

8、分组

// 将员工按薪资是否高于8000分组
Map<Boolean, List<Person>> groupBySalary = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
System.out.println("按员工薪资是否大于8000分组情况:" + groupBySalary);// 将员工按性别分组
Map<String, List<Person>> groupBySex = personList.stream().collect(Collectors.groupingBy(Person::getSex));
System.out.println("按员工性别分组情况:" + groupBySex);// 将员工先按性别分组,再按地区分组
Map<String, Map<String, List<Person>>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
System.out.println("按员工性别、地区:" + group);

9、排序

// 按工资升序排序(自然排序)
List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
System.out.println("按工资升序排序:" + newList);// 按工资倒序排序
List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
System.out.println("按工资降序排序:" + newList2);// 先按工资再按年龄升序排序
List<String> newList3 = personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName).collect(Collectors.toList());
System.out.println("先按工资再按年龄升序排序:" + newList3);// 先按工资再按年龄自定义排序(降序)
List<String> newList4 = personList.stream().sorted((p1, p2) -> {if (p1.getSalary() == p2.getSalary()) {return p2.getAge() - p1.getAge();} else {return p2.getSalary() - p1.getSalary();}
}).map(Person::getName).collect(Collectors.toList());
System.out.println("先按工资再按年龄自定义降序排序:" + newList4);/*
* 按工资升序排序:[王五, 李四, 小红, 小明, 张三, 小刚]
* 按工资降序排序:[小刚, 张三, 小明, 小红, 李四, 王五]
* 先按工资再按年龄升序排序:[王五, 李四, 小红, 小明, 张三, 小刚]
* 先按工资再按年龄自定义降序排序:[小刚, 张三, 小明, 小红, 李四, 王五]
*/

上面有些方法其实使用并不恰当,在这里只是起到了抛砖引玉的作用,大家选取最适用的的即可。

—————————————————————————————————————————————
与Spark RDD算子比较。

其实如果你有使用Spark的经验的话,那么你肯定和我一样,使用Java Stream时,会有一种似曾相识的感觉。

Spark API 的所有操作都是基于RDD的。对RDD进行函数操作分为两种:Transformation和Action。Spark在遇到Transformation操作时只会记录需要这样的操作,并不会去执行,需要等到有Action操作的时候才会真正启动计算过程进行计算。

注:RDD俗称弹性分布式数据集,数据不只存储在一台机器上,而是分布在多台机器上,实现数据计算的并行化。

你可以发现这个描述和文章开始对Java Steam的描述,过程极其相似。他们的方法也有很多相似的,大家可以做个对比。

Transformation算子:map()、filter()、flatMap()、sample()、union()、groupByKey()、reduceByKey( )、join()
Actions算子:reduce()、collect()、count()、take(n)、first()、saveAsTextFile()、foreach()、saveAsSequenceFile()

Java Stream流的使用相关推荐

  1. java Stream 流

    java Stream 流 Stream 流 流的创建 流的转化 Optianal 流的计算 Stream 流 1. Stream的定义 来自数据源的支持聚合操作的元素序列. 即一个流对外提供接口,接 ...

  2. 测试Java Stream流 parralle与 sequential的效率

    测试Java Stream流 parralle与 sequential的效率 实验环境 操作系统:win10 处理器:Intel i5-4200U 2核4线程 Java版本:1.8 实验方案 统计大小 ...

  3. Java stream流式计算详解

    Java stream流式计算详解 1. Stream概述 1.1 Stream简介 1.2 Stream分类 2. Stream操作 2.1 Stream创建 2.2 Stream无状态操作 2.3 ...

  4. Java Stream流基础

    Java Stream流基础 何为Steam流? Stream 是对集合数组对象功能的增强,其专注于对集合数组对象进行各种非常便利.高效的聚合操作,或者大批量数据操作.通常我们需要多行代码才能完成的操 ...

  5. Java Stream流之求和

    Java Stream流之求和 流方式实现 基本数据类型 和 包装类型 的一位数组求和 package top.yangbocsu;import java.util.Arrays;/*** @auth ...

  6. Java Stream流的概念

    为什么80%的码农都做不了架构师?>>>    1. Java的流分为 Inputstream 和 OutputStream: 2. 流(stream)的概念源于UNIX中管道(pi ...

  7. Java Stream(流)的分类, 四大基本流的介绍

    上一篇文章已经介绍过什么是流, 以及流的基本概念 http://blog.csdn.net/nvd11/article/details/29917065 本文主要介绍java四大基本流的方法. 一, ...

  8. Java Stream流总结

    Java8新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利. 文章目录 前言 一.stream是什么? 二.使用步骤 1.得到sream 2 ...

  9. Java -Stream流和常见函数式接口

    概念 流(Stream)与集合类似,但集合中保存的是数据,而Stream中保存对集合或数组数据的操作. 特点 tream 自己不会存储元素. Stream 不会改变源对象.相反,他们会返回一个持有结果 ...

  10. 深度掌握 Java Stream 流操作,让你的代码高出一个逼格

    概念 Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选.排序.聚合等. Stream` 的操作符大体上分为两种:`中间操作符`和`终止操 ...

最新文章

  1. 奶牛异或(01字典树)
  2. spring文件下载记录
  3. mysql scrapy 重复数据_mysql数据库如何处理重复数据?
  4. HTML5新增标签的汇总与详解
  5. python之模块calendar(汇集了日历相关的操作)
  6. 关于python_关于python的基础知识
  7. PHP_VERSION获取php版本
  8. 关于Etcd的几个问题
  9. 浏览器滚动的详细解释 Vue 固定滚动位置的实现
  10. git ssh配置完后拉取代码_使用git在gitlab上拉取代码的方法
  11. 使用php的curl根据关键词爬取百度搜索结果页
  12. Python获取矢量文件属性表字段及类型
  13. Uniapp 微信小程序登陆页面
  14. 计算机组装系统安装系统,刚组装电脑怎么装系统?
  15. google chrome浏览器崩溃修复
  16. matlab实现zca去白化,白化算法
  17. python如何编写温度转换_用python写温度转换
  18. 视频教程 | 与程序员进行高效沟通,三分钟带你掌握Zeplin
  19. C++ 点(.)操作符和箭头(-)操作符
  20. C#最小二乘法进行曲线拟合及相关系数

热门文章

  1. codeforces:F. All Possible Digits【贪心 + 模拟进位】
  2. 教师信息管理系统(C++)源代码加论文
  3. LOL狗年官网刷一波
  4. keil中看c语言汇编,keil中查看C语言对应汇编语言问题
  5. mac上安装启动redis
  6. 【初试复试第一】脱产在家二战上岸——上交819考研经验
  7. freeswitch源码阅读 之 sofia模块
  8. mysql 社区版密码如何修改_mysql修改密码
  9. Rockland ELISA试剂丨Rockland ELISA包被稳定剂方案
  10. DB2 降低高水位线、刷新统计、索引信息