前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java8 Stream

Java8 Stream

原创
作者头像
HLee
修改2021-09-15 09:56:06
1.7K0
修改2021-09-15 09:56:06
举报
文章被收录于专栏:房东的猫

简介

Java Stream函数式编程接口最初是在Java 8中引入的,并且与lambda一起成为Java开发的里程碑式的功能特性,它极大的方便了开放人员处理集合类数据的效率。

  • 源操作:可以将数组、集合类、行文本文件转换成管道流Stream进行数据处理
  • 中间操作:对Stream流中的数据进行处理,比如:过滤、数据转换等等
  • 终端操作:作用就是将Stream管道流转换为其他的数据类型。

Stream源操作

Java Stream就是一个数据流经的管道,并且在管道中对数据进行操作,然后流入下一个管道。管道的功能包括:Filter(过滤)、Map(映射)、sort(排序)等,集合数据通过Java Stream管道处理之后,转化为另一组集合或数据输出。

将集合转化为流

首先,java.util.Collection接口中加入了default的stream方法,也就是说Collection接口下的所有实现都可以通过stream方法来获取Stream流。

代码语言:javascript
复制
public interface Collection<E> extends Iterable<E> {
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}



Map<String, Object> map = new HashMap<>();
map.keySet().stream();
map.values().stream();
map.entrySet().stream();
代码语言:javascript
复制
List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");

List<String> list = nameStrs.stream()
        .filter(s -> s.startsWith("L"))
        .map(String::toUpperCase)
        .sorted()
        .collect(toList());

Set<String> set = new HashSet<>(list);
Stream<String> streamFromSet = set.stream();
  • 首先,我们使用Stream()函数,将一个List转换为管道流
  • 调用filter函数过滤数组元素,过滤方法使用lambda表达式,以L开头的元素返回true被保留,其他的List元素被过滤掉
  • 然后调用Map函数对管道流中每个元素进行处理,字母全部转换为大写
  • 然后调用sort函数,对管道流中数据进行排序
  • 最后调用collect函数toList,将管道流转换为List返回

将数组转化为流

代码语言:javascript
复制
String[] array = {"Monkey", "Lion", "Giraffe", "Lemur"};
Stream<String> nameStrs2 = Stream.of(array);

Stream<String> nameStrs3 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur");

将文本文件转化为流

通过Files.lines方法将文本文件转换为管道流,下图中的Paths.get()方法作用就是获取文件,是Java NIO的API。也就是说:我们可以很方便的使用Java Stream加载文本文件,然后逐行的对文件内容进行处理。

代码语言:javascript
复制
Stream<String> lines = Files.lines(Paths.get("file.txt"));

Stream中间操作

对Stream流中的数据进行处理,比如:过滤、数据转换等等。

Filter函数

代码语言:javascript
复制
@Data
@AllArgsConstructor
public class Employee {

   private Integer id;
   private Integer age;   //年龄
   private String gender;  //性别
   private String firstName;  
   private String lastName;
}
代码语言:javascript
复制
public class StreamFilterPredicate {
    
    public static void main(String[] args){
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");

        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

        List<Employee> filtered = employees.stream()
                .filter(e -> e.getAge() > 70 && e.getGender().equals("M"))
                .collect(Collectors.toList());

        System.out.println(filtered);
    }
}

1. 谓词逻辑

lambda表达式表达的是一个匿名接口函数的实现。那具体到Stream.filter()中,它表达的是什么呢?可以看出它表达的是一个Predicate接口,在英语中这个单词的意思是:谓词。

代码语言:javascript
复制
// @return the new stream
Stream<T> filter(Predicate<? super T> predicate);

通常情况下,filter函数中lambda表达式为一次性使用的谓词逻辑。如果我们的谓词逻辑需要被多处、多场景、多代码中使用,通常将它抽取出来单独定义到它所限定的主语实体中。

代码语言:javascript
复制
比如:将下面的谓词逻辑定义在Employee实体class中。

public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() >70;
public static Predicate<Employee> genderM = x -> x.getGender().equals("M");

2. and语法(交集)

代码语言:javascript
复制
List<Employee> filtered = employees.stream()
        .filter(Employee.ageGreaterThan70.and(Employee.genderM))
        .collect(Collectors.toList());

3. or语法(并集)

代码语言:javascript
复制
List<Employee> filtered = employees.stream()
        .filter(Employee.ageGreaterThan70.or(Employee.genderM))
        .collect(Collectors.toList());

4. negate语法(取反)

代码语言:javascript
复制
List<Employee> filtered = employees.stream()
        .filter(Employee.ageGreaterThan70.or(Employee.genderM).negate())
        .collect(Collectors.toList());

Map函数

map函数作用就是针对管道流中的每一个数据元素进行某种操作,然后返回一个新流。

1. map函数

map函数处理数据。如下:

代码语言:javascript
复制
// @return the new stream
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
代码语言:javascript
复制
List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");

// 使用Stream管道流
List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList());
//上面使用了方法引用,和下面的lambda表达式语法效果是一样的
//List<String> collect = alpha.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());

map()函数转换数据的类型。如下:

代码语言:javascript
复制
alpha.stream()
        .map(String::length)
        .forEach(System.out::println);


Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
        .mapToInt(String::length)
        .forEach(System.out::println);
        
mapToInt():接受一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream
mapToLong():接受一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream
maoToDouble():接受一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream

map函数处理对象数据格式转化。如下:

  • 将每一个Employee的年龄增加一岁
  • 将性别中的“M”换成“male”,F换成Female
代码语言:javascript
复制
public static void main(String[] args){
    Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
    Employee e2 = new Employee(2,13,"F","Martina","Hengis");
    Employee e3 = new Employee(3,43,"M","Ricky","Martin");
    Employee e4 = new Employee(4,26,"M","Jon","Lowman");
    Employee e5 = new Employee(5,19,"F","Cristine","Maria");
    Employee e6 = new Employee(6,15,"M","David","Feezor");
    Employee e7 = new Employee(7,68,"F","Melissa","Roy");
    Employee e8 = new Employee(8,79,"M","Alex","Gussin");
    Employee e9 = new Employee(9,15,"F","Neetu","Singh");
    Employee e10 = new Employee(10,45,"M","Naveen","Jain");

    List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

    // 使用map方式
    List<Employee> maped = employees.stream()
            .map(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M") ? "male" : "female");
                return e;
            }).collect(Collectors.toList());

    // 使用peek方式,可以省掉return
    List<Employee> maped = employees.stream()
            .peek(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M") ? "male" : "female");
            }).collect(Collectors.toList());

    System.out.println(maped);
}

由于map的参数e就是返回值,所以可以用peek函数。peek函数是一种特殊的map函数,当函数没有返回值或者参数就是返回值的时候可以使用peek函数。

2. flatMap函数

map可以对管道流中的数据进行转换操作,但是如果管道中还有管道该如何处理?即:如何处理二维数组及二维集合类。实现一个简单的需求:将“hello”,“world”两个字符串组成的集合,元素的每一个字母打印出来。如果不用Stream我们怎么写?写2层for循环,第一层遍历字符串,并且将字符串拆分成char数组,第二层for循环遍历char数组。

代码语言:javascript
复制
List<String> words = Arrays.asList("hello", "word");
words.stream()
        .map(w -> Arrays.stream(w.split("")))    //[[h,e,l,l,o],[w,o,r,l,d]]
        .forEach(System.out::println);

输出结果:
java.util.stream.ReferencePipeline$Head@3551a94
java.util.stream.ReferencePipeline$Head@531be3c5

说明:Arrays.stream() 返回的是一个新的stream

用map方法是做不到的,这个需求用map方法无法实现。map只能针对一维数组进行操作,数组里面还有数组,管道里面还有管道,它是处理不了每一个元素的。

flatMap可以理解为将若干个子管道中的数据全都平面展开到父管道中进行处理。

代码语言:javascript
复制
words.stream()
        .flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
        .forEach(System.out::print);
        
输出结果:helloword

Limit与Skip函数

代码语言:javascript
复制
List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
        .limit(2)
        .collect(Collectors.toList());
List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
        .skip(2)
        .collect(Collectors.toList());
  • limt方法传入一个整数n,用于截取管道中的前n个元素。经过管道处理之后的数据是:[Monkey, Lion]。
  • skip方法与limit方法的使用相反,用于跳过前n个元素,截取从n到末尾的元素。经过管道处理之后的数据是: [Giraffe, Lemur]

Distinct函数

使用distinct方法对管道中的元素去重,涉及到去重就一定涉及到元素之间的比较,distinct方法时调用Object的equals方法进行对象的比较的,如果你有自己的比较规则,可以重写equals方法。

Stream流中的distinct方法对于基本数据类型可以直接去重,但是对于自定义类型,我们需要重写hashCode和equals方法。

代码语言:javascript
复制
List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .distinct()
        .collect(Collectors.toList());

Sorted函数

说明:产生一个新流,其按指定顺序排序。

默认的情况下,sorted是按照字母的自然顺序进行排序。如下代码的排序结果是:[Giraffe, Lemur, Lion, Monkey],字数按顺序G在L前面,L在M前面。第一位无法区分顺序,就比较第二位字母。

代码语言:javascript
复制
List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
        .sorted()
        .collect(Collectors.toList());
        
//自然排序
List<Integer> list = Arrays.asList(1, 3,24, 5, 6, 8, 23, 45, 72, 16);
Stream<Integer> stream = list.stream();
stream.sorted().forEach(System.out::println);

//对象排序:对象排序可以先实现comparable接口,或者直接指定
List<Student> studentList = getStudents();
//实现comparable接口
studentList.stream().sorted().forEach(System.out::println);
//直接指定
studentList.stream().sorted((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())).forEach(System.out::println);

List<String> list = Arrays.asList("c", "e", "a", "d", "b");
list.stream().sorted((s1, s2) -> s1.compareTo(s2)).forEach(System.out::println);

Parallel函数

  • 串行的好处是可以保证顺序,但是通常情况下处理速度慢一些
  • 并行的好处是对于元素的处理速度快一些(通常情况下),但是顺序无法保证。这可能会导致进行一些有状态操作的时候,最后得到的不是你想要的结果。
代码语言:javascript
复制
@Test
public void test4() {

    List<Integer> list = Lists.newArrayList();

    // 通过List接口直接获取并行流
    Stream<Integer> integerStream = list.parallelStream();
    // 将已有的串行流转换为并行流
    Stream<Integer> integerStream1 = Stream.of(1, 2, 3).parallel();
}

parallel()函数表示对管道中的元素进行并行处理,而不是串行处理。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证。

代码语言:javascript
复制
总结:
    1. Stream并行流计算 >> 普通for循环 ~= Stream串行流计算 (之所以用两个大于号,你细品)
    2. 数据容量越大,Stream流的执行效率越高
    3. Stream并行流计算通常能够比较好的利用CPU的多核优势。CPU核心越多,Stream并行流计算效率越高
代码语言:javascript
复制
/**
 * 并行流 安全(同步锁)
 */
@Test
public void test5() {

    List<Integer> listNew = Lists.newArrayList();
    Object obj = new Object();

    IntStream.rangeClosed(1, 1000).parallel().forEach(i -> {
        synchronized (obj) {
            listNew.add(i);
        }
    });
    System.out.println(listNew.size());
}

/**
 * 并行流 安全(Collections.synchronizedList(listNew))
 */
@Test
public void test6() {

    List<Integer> listNew = Lists.newArrayList();
    List<Integer> safeList = Collections.synchronizedList(listNew);

    IntStream.rangeClosed(1, 1000).parallel().forEach(i -> {
        safeList.add(i);
    });
    System.out.println(safeList.size());
}

/**
 * 并行流 安全(Vector)
 */
@Test
public void test7() {

    List<Integer> listNew = new Vector<>();

    IntStream.rangeClosed(1, 1000).parallel().forEach(i -> {
        listNew.add(i);
    });
    System.out.println(listNew.size());
}

/**
 * 并行流 安全
 */
@Test
public void test8() {
    List list = IntStream.rangeClosed(1, 1000).parallel().boxed().collect(Collectors.toList());
    System.out.println(list.size());
}

Stream的终止操作

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

匹配和查找

代码语言:javascript
复制
List<Student> list = getStudents();

//判断所有学生年龄是否都大于12岁
boolean allMatch = list.stream().allMatch(item -> item.getAge() > 12);

//判断是否存在学生的年龄大于12岁
boolean anyMatch = list.stream().anyMatch(item -> item.getAge() > 20);

//判断是否有学生叫"小李"
boolean noneMatch = list.stream().noneMatch(item -> item.getName().equals("小李"));

//查找第一个年龄大于20的学生
//findFirst用于查找第一个符合“匹配规则”的元素,返回值为Optional
Optional<Student> first = list.stream().filter(e -> e.getAge() > 20).findFirst();

//查找学生数据
long count = list.stream().count();
long count1 = list.stream().filter(item -> item.getScore() > 90).count();

//查找当前流中的元素
//findAny用于查找任意一个符合“匹配规则”的元素,返回值为Optional
Optional<Student> any = list.stream().findAny();

//查找学生最高的分数:Student实现了comparable接口的话,可以直接比较
Stream<Double> doubleStream = list.stream().map(item -> item.getScore());
doubleStream.max(Double::compareTo);

归约(reduce)

Stream API为我们提供了Stream.reduce用来实现集合元素的归约。reduce函数有三个参数:

  • Identity标识:一个元素,它是归约操作的初始值,如果流为空,则为默认结果。
  • Accumulator累加器:具有两个参数的函数:归约运算的部分结果和流的下一个元素。
  • Combiner合并器(可选):当归约并行化时,或当累加器参数的类型与累加器实现的类型不匹配时,用于合并归约操作的部分结果的函数。

阶段累加结果作为累加器的第一个参数;集合遍历元素作为累加器的第二个参数。

1. Integer类型归约

reduce初始值为0,累加器可以是lambda表达式,也可以是方法引用。

代码语言:javascript
复制
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int result = numbers.stream().reduce(0, (subtotal, element) -> subtotal + element);
System.out.println(result);  //21

int result = numbers.stream().reduce(0, Integer::sum);
System.out.println(result); //21

案例:
@Test
public void test() {

    // 第一次的时候会将默认值给x;之后每次会将上一次操作结果赋值给x,y就是每次从数据中获取元素
    Integer sum = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
        System.out.println("x: " + x + " --- y: " + y);
        return x + y;
    });
    System.out.println(sum);

    // 取出数据元素中的最大值
    Integer max = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
        return x > y ? x : y;
    });
    System.out.println(max);
}

x: 0 --- y: 4
x: 4 --- y: 5
x: 9 --- y: 3
x: 12 --- y: 9
21
9

2. String类型归约

只要累加器参数类型能够匹配,可以对任何类型的集合进行归约计算。

代码语言:javascript
复制
List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
String result = letters.stream().reduce("", (partialString, element) -> partialString + element);
System.out.println(result);  //abcde

String result = letters.stream().reduce("", String::concat);
System.out.println(result);  //ancde

3. 复杂对象归约

计算所有的员工的年龄总和。

代码语言:javascript
复制
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");

List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

Integer total = employees.stream().map(Employee::getAge).reduce(0, Integer::sum);
System.out.println(total); //346

4. 并行流数据归约(使用合并器)

对于大数据量的集合元素归约计算,更能体现出Stream并行流计算的威力。

在进行并行流计算的时候,可能会将集合元素分成多个组计算。为了更快的将分组计算结果累加,可以使用合并器。

代码语言:javascript
复制
Integer total2 = employees
        .parallelStream()
        .map(Employee::getAge)
        .reduce(0, Integer::sum, Integer::sum);  //注意这里reduce方法有三个参数

System.out.println(total); //346

ForEach和ForEachOrdered

如果我们只是希望将Stream管道流的处理结果打印出来,而不是进行类型转换,我们就可以使用forEach()方法或forEachOrdered()方法。

代码语言:javascript
复制
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .parallel()
        .forEach(System.out::println); // 遍历流中的数据
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .parallel()
        .forEachOrdered(System.out::println);
  • parallel()函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证
  • forEachOrdered从名字上看就可以理解,虽然在数据处理顺序上可能无法保障,但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致(forEach方法则无法保证这个顺序)。

Collect函数

将管道流处理结果在转换成集合类。

1. 收集到Set

通过Collectors.toSet()方法收集Stream的处理结果,将所有元素收集到Set集合中。

代码语言:javascript
复制
Set<String> collectToSet = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .collect(Collectors.toSet());

//最终collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur],注意Set会去重。

2. 收集到List

可以将元素收集到List使用toList()收集器中。

代码语言:javascript
复制
List<String> collectToList = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion").collect(Collectors.toList());

// 最终collectToList中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

3. 收集到Array

通过toArray(String[]::new)方法收集Stream的处理结果,将所有元素收集到字符串数组中。

代码语言:javascript
复制
String[] toArray = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion").toArray(String[]::new);

//最终toArray字符串数组中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

4. 收集到Map

使用Collectors.toMap()方法将数据元素收集到Map里面。但是出现一个问题:那就是管道中的元素是作为key,还是作为value。我们用到了一个Function.identity()方法,该方法很简单就是返回一个“ t -> t ”(输入就是输出的lambda表达式)。另外使用管道流处理函数distinct()来确保Map键值的唯一性。

代码语言:javascript
复制
Map<String, Integer> toMap = Stream.of(
    "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.distinct()
.collect(Collectors.toMap(
       Function.identity(),   //元素输入就是输出,作为key
       s -> (int) s.chars().distinct().count()// 输入元素的不同的字母个数,作为value
));

// 最终toMap的结果是: {Monkey=6, Lion=4, Lemur=5, Giraffe=6}   

5. 分组收集groupingBy

Collectors.groupingBy用来实现元素的分组收集。下面的代码演示如何根据首字母将不同的数据元素收集到不同的List,并封装为Map。

代码语言:javascript
复制
Map<Character, List<String>> groupingByList =  Stream.of(
    "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.groupingBy(
       s -> s.charAt(0) ,  //根据元素首字母分组,相同的在一组
       // counting()        // 加上这一行代码可以实现分组统计
));

// 最终groupingByList内的元素: {G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]}
// 如果加上counting() ,结果是:  {G=1, L=3, M=1}

这是该过程的说明:groupingBy第一个参数作为分组条件,第二个参数是子收集器。

代码语言:javascript
复制
多级分组:

@Test
public void test() {

    Stream.of(
            new Employee(1, 23, "M", "Rick", "Beethovan"),
            new Employee(2, 25, "W", "Rick", "Beethovan"),
            new Employee(3, 13, "M", "Rick", "Beethovan"),
            new Employee(4, 26, "W", "Rick", "Beethovan"),
            new Employee(5, 43, "M", "Rick", "Beethovan")
    ).collect(Collectors.groupingBy(Employee::getGender, Collectors.groupingBy(p -> p.getAge() > 18 ? "成年" : "未成年"))).forEach((k, v) -> {
        System.out.println(k);
        v.forEach((k1, v1) -> {
            System.out.println("\t" + k1 + "=" + v1);
        });
    });
}

W
	成年=[com.java.master.Stream.Employee@2ac1fdc4, com.java.master.Stream.Employee@5f150435]
M
	未成年=[com.java.master.Stream.Employee@1c53fd30]
	成年=[com.java.master.Stream.Employee@50cbc42f, com.java.master.Stream.Employee@75412c2f]

6. 通用的收集方式

向所需Collection类型提供构造函数的方式。

代码语言:javascript
复制
LinkedList<String> collectToCollection = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion").collect(Collectors.toCollection(LinkedList::new));

//最终collectToCollection中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

注意:代码中使用了LinkedList::new,实际是调用LinkedList的构造函数,将元素收集到LinkedList。当然你还可以使用诸如LinkedHashSet::newPriorityQueue::new将数据元素收集为其他的集合类型,这样就比较通用了。

7. 分区操作

Collectors.partitioningBy会根据值是否为true,把集合中的数据分割为两个列表,一个true列表和一个false列表。

代码语言:javascript
复制
@Test
public void test2() {

    Stream.of(
            new Employee(1, 23, "M", "Rick", "Beethovan"),
            new Employee(2, 25, "W", "Rick", "Beethovan"),
            new Employee(3, 13, "M", "Rick", "Beethovan"),
            new Employee(4, 26, "W", "Rick", "Beethovan"),
            new Employee(5, 43, "M", "Rick", "Beethovan")
    ).collect(Collectors.partitioningBy(p -> p.getAge() > 18)).forEach((k, v) -> {
        System.out.println(k + "\t" + v);
    });
}

false	[com.java.master.Stream.Employee@c39f790]
true	[com.java.master.Stream.Employee@71e7a66b, com.java.master.Stream.Employee@2ac1fdc4, com.java.master.Stream.Employee@5f150435, com.java.master.Stream.Employee@1c53fd30]

8. 拼接joining

代码语言:javascript
复制
/**
 * joining
 */
@Test
public void test3() {

    String re = Stream.of(
            new Employee(1, 23, "M", "Rick", "Beethovan"),
            new Employee(2, 25, "W", "Rick", "Beethovan"),
            new Employee(3, 13, "M", "Rick", "Beethovan"),
            new Employee(4, 26, "W", "Rick", "Beethovan"),
            new Employee(5, 43, "M", "Rick", "Beethovan")
    ).map( m -> m.getFirstName()).collect(Collectors.joining(" -- "));

    System.out.println(re);
}

其他常用函数

代码语言:javascript
复制
// 将两个流进行合并
List<String> list = Arrays.asList("a", "b");
List<String> list2 = Arrays.asList("c", "d");
Stream<String> concatStream = Stream.concat(list.stream(), list2.stream());
concatStream.forEach(System.out::println);
输出:
a
b
c
d

// 管道中元素数据累加结果sum: 6
int sum = IntStream.of(1, 2, 3).sum();

//管道中元素数据平均值average: OptionalDouble[2.0]
OptionalDouble average = IntStream.of(1, 2, 3).average();

//管道中元素数据最大值max: 3
int max = IntStream.of(1, 2, 3).max().orElse(0);

// 全面的统计结果statistics: IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}
IntSummaryStatistics statistics = IntStream.of(1, 2, 3).summaryStatistics();

// 获取最大值
Optional optional = Stream.of(4, 5, 3, 9).collect(Collectors.maxBy((x, y) -> x - y));
System.out.println(optional);

// 获取最小值
Optional optional = Stream.of(4, 5, 3, 9).collect(Collectors.minBy((x, y) -> x - y));
System.out.println(optional);

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • Stream源操作
    • 将集合转化为流
      • 将数组转化为流
        • 将文本文件转化为流
        • Stream中间操作
          • Filter函数
            • 1. 谓词逻辑
            • 2. and语法(交集)
            • 3. or语法(并集)
            • 4. negate语法(取反)
          • Map函数
            • 1. map函数
            • 2. flatMap函数
          • Limit与Skip函数
            • Distinct函数
              • Sorted函数
                • Parallel函数
                • Stream的终止操作
                  • 匹配和查找
                    • 归约(reduce)
                      • 1. Integer类型归约
                      • 2. String类型归约
                      • 3. 复杂对象归约
                      • 4. 并行流数据归约(使用合并器)
                    • ForEach和ForEachOrdered
                      • Collect函数
                        • 1. 收集到Set
                        • 2. 收集到List
                        • 3. 收集到Array
                        • 4. 收集到Map
                        • 5. 分组收集groupingBy
                        • 6. 通用的收集方式
                        • 7. 分区操作
                        • 8. 拼接joining
                      • 其他常用函数
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档