java流库

流与集合的差异

  • 流并不储存其元素,这些元素可能存储于底层的集合中,或者按需生成的
  • 流的操作不会修改数据源
  • 流的操作是尽可能惰性的,这意味着直至需要其结果时,操作才进行。

流的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");

Stream<String> stream = list.stream();//通过集合创建流

Stream<String> stream1 = Stream.of(new String[]{"123","456","789"});//通过数组创建流

Stream<String> stream2 = Stream.of("123","456","789");//通过具体数据创建流

Stream<Double> stream3 = Stream.generate(Math::random); //创建无限流,上面的都是有限流

Stream<Integer> stream4 = Stream.iterate(0, integer -> ++integer);//创建无限序列

Stream<String> stream5 = Stream.empty();//产生空的流

String[] strings = new String[]{"123","456","789"};//使用Arrays创建流
Stream stream6 = Arrays.stream(strings);

filter

filter方法产生一个流,其中包含当前流中满足指定条件的所有元素

1
2
3
4
5
6
7
8
9
10
11
 List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
Stream filterTest = list.stream().filter(p->p.length() >= 4);//获取长度大于等于4的字符串的流
filterTest.forEach(System.out::println);

输出结果:

12345
6789

map

将目标流转化为指定流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
Stream mapTest = list.stream().map(String::length);
mapTest.forEach(System.out::println);

等同于

List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
Stream mapTest = list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});
mapTest.forEach(System.out::println);

flatMap

1
2
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

flatMapmap的区别:

map是将mapper应用于当前流中所有的元素产生的结果,而flatMap是通过将mapper应用于当前流
中所有元素产生的结果连接在一起而获得。

limit、skip和concat

1
2
3
4
5
6
7
8
9
List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
list.stream().limit(2).forEach(System.out::println);//limit只保留前面指定元素个数
list.stream().skip(1).forEach(System.out::println);//skip跳过前面指定元素个数
System.out.println("===============");
//concat连接两个流(a,b),它产生的流是a元素后面跟着b元素,注意:第一个流不能是无限流,比如第二个元素永远不能得到处理的机会
Stream.concat(list.stream(),list.stream()).forEach(System.out::println);

流转换

1
2
3
4
5
6
7
8
Stream<T> distinct() //产生一个流,包含原来流中所有不同的元素

Stream<T> sorted()//排序流,要求流中元素都实现 Comparable 接口
Stream<T> sorted(Comparator<? super T> comparator)//排序流

//peek方法会产生另一个流,它的元素与原来流中相同,但是在每一次获取一个元素时
//都会调用action中的方法,这对于调试很方便
Stream<T> peek(Consumer<? super T> action)

流操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");

Optional<String> v = list.stream().max(Comparator.comparingInt(String::length));
Optional<String> v1 = list.stream().min(Comparator.comparingInt(String::length));
Optional<String> v2 = list.stream().findFirst();
Optional<String> v3 = list.stream().findAny();
boolean v4 = list.stream().anyMatch(t->t.length()>3);//是否有任意个匹配
boolean v5 = list.stream().noneMatch(t->t.length()>3);//是否没有一个匹配
boolean v6 = list.stream().allMatch(t->t.length()>3);//是否都匹配
System.out.println(v.get());
System.out.println(v1.get());
System.out.println(v2.get());
System.out.println(v3.get());
System.out.println(v4);
System.out.println(v5);
System.out.println(v6);

结果为:

12345
123
12345
12345
true
false
false

Optional

创建Optional:

1
2
3
4
5
6
//of 方法产生具有给定值的Optional
Optional<String> value = Optional.of("123");
//产生一个空的Optional,不能使用 get 方法,否则会保错
Optional<String> value1 = Optional.empty();
//如果给定的值为null,则产生一个空的Optional,否则产生具有给定值的Optional
Optional<String> value2 = Optional.ofNullable(null);

常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Optional<String> value = Optional.empty();
String result = value.orElse("");//如果Optional的给定值为null,则会返回默认值 ""
String result1 = value.orElseGet(()->"");//如果Optional的给定值为null,则会调用指定方法返回值
String result2 =value.orElseThrow(IllegalStateException::new);//如果Optional的给定值为null,则会抛出错误
value.ifPresent(System.out::println);//只有Optional的值不为null时,才执行指定方法

Optional<String> value = Optional.of("12345");
//Optional的 map 和流的 map 方法差不多,它可以转换Optional的类型,例如下面从 Optional<String> 转换为 Optional<Integer>
//注意:如果上面的 value 为空的Optional,或者 value.map(v->null) ,则会返回空的Optional(即Optional.empty())
Optional<Integer> optional = value.map(String::length);
System.out.println(optional);

//和map差不多
Optional<String> value = Optional.of("12345");
Optional<Integer> integer = value.flatMap(s->Optional.of(s.length()));

流转换为集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
    //流转化为数组
String[] array = (String[]) stream.toArray();
String[] arrays = stream.toArray(String[]::new);

//流转化为指定的集合
List<String> list1 = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
//映射表
Map<Integer,String> map = stream.collect(Collections.toMap(Person::getId,Person::getName))
Map<Integer,Person> map = stream.collect(Collections.toMap(Person::getId,Function.identity()))
//第三个参数用来处理键发生冲突的情况,默认是抛出异常
Map<Integer,String> map = stream.collect(Collections.toMap(Person::getId,Person::getName,(existValue,newValue)->existValue))
//转化为指定的 Map
Map<Integer,String> map = stream.collect(Collections.toMap(Person::getId,Person::getName),TreeMap::new)

//连接流中的元素,可以指定分隔符
String result = stream.collect(Collectors.joining());
String result1 = stream.collect(Collectors.joining(","));

//有(Double|Long|Int)三种,下面只列举了Int,其他类似
IntSummaryStatistics summaryStatistics = stream.collect(Collectors.summarizingInt(String::length));
double average = summaryStatistics.getAverage();//平均值
double maxWordLength = summaryStatistics.getMax();//最大值
long sum = summaryStatistics.getSum();//总和
long count = summaryStatistics.getCount();//元素个数

//通过群组和分区来指定映射表
List<Student> students = new ArrayList<>();
Student student1=new Student(1,"小明");
Student student2=new Student(2,"小军");
Student student3=new Student(1,"小红");
Student student4=new Student(1,"小键");
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
//通过班级的名字来分组
Map<Integer,List<Student>> cl=students.stream().collect(Collectors.groupingBy(Student::getClassName));
//通过判断班级名是否为2来分组
Map<Boolean,List<Student>> cl1=students.stream().collect(Collectors.partitioningBy(l->l.getClassName()==2));

List<Teacher> teachers = new ArrayList<>();
Teacher teacher1=new Teacher("北京","小明",26);
Teacher teacher2=new Teacher("重庆","小军",30);
Teacher teacher3=new Teacher("上海","小红",26);
Teacher teacher4=new Teacher("北京","小键",34);
teachers.add(teacher1);
teachers.add(teacher2);
teachers.add(teacher3);
teachers.add(teacher4);
//通过工作地点把教师分组后,使用 Collectors.counting() 方法计算每组的人数
//和 Collectors.counting() 类似的对分好组的集合进行操作的方法有:
//summingInt/Long/Double 通过传递一个 返回值为 int/long/double 的函数来对已经分好组的集合进行操作,并求其和
//maxBy和minBy 获取分好组集合中的最大值或最小值
Map<String,Long> c=teachers.stream().collect(Collectors.groupingBy(Teacher::getWorkplace,Collectors.counting()));
//通过工作地点把教师分组后,通过 Collectors.mapping() 方法对已经分好组的集合使用 getAge()方法获取这个集合中各个老师的年龄
//并放在一个新的集合中
Map<String,Set<Integer>> c =
teachers.stream().collect(Collectors.groupingBy(Teacher::getWorkplace,Collectors.mapping(Teacher::getAge,Collectors.toSet())));
}

基本类型流

IntStream:可以存储shortcharbyteboolean
DoubleStream:存储floatdouble
LongStream:存储long

基本类型流的方法和上面介绍的方法类似,下面介绍基本类型流的特有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//独有构造方法
//range()、rangeClosed(),IntStream和LongStream独有
IntStream t=IntStream.range(0,10);//范围 [0,10)
IntStream t1=IntStream.rangeClosed(0,10);//[0,10]

//codePonts chars 生成由字符的Unicode码或由UTF-16编码机制的码元构成的IntStream,这两个方法的区别暂时还不清楚
String s = "gkdl;iofk;slaksdk,sdki\uD835";
IntStream i1 = s.chars();
IntStream i2 = s.codePoints();

//mapToInt mapToLong mapToDouble
IntStream stream = teachers.stream().mapToInt(Teacher::getAge);

//将基本类型流转化为对象流
Stream<Integer> t=IntStream.range(0,10).boxed();

//这里的OptionalInt和Optional类似
OptionalInt o1= t.max();//最大值
OptionalInt o2=t.min();//最小值
OptionalDouble o3=t.average();//平均值
int sum = t.sum();//总和

//返回由随机数构成的基本类型
Random random = new Random();
IntStream r1 = random.ints();
DoubleStream r2 = random.doubles();
LongStream r3 = random.longs();

并行流

1
2
3
4
//创建并行流
Collection.parallelStream()//集合转为并行流

Stream.parallel()//流转为并行流

并行流正常工作的条件

  • 数据应该在内存中,等待数据到达是低效的
  • 流一个分为高效的几部分,由数组或平衡二叉树支撑的流都可以正常工作,但是Stream.iterate不行
  • 流操作的工作量应该有很大的规模,不然使用并行流没有意义
  • 流操作不应该被堵塞