生成流

通过集合生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// list 列表转换为 Stream
List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("b");
Stream stream3 = list.stream();

// Set 集合转换为 Stream
Set<String> strSet = new HashSet<>();
strSet.add("a");
strSet.add("b");
Stream stream4 = strSet.stream();

// Map 集合转换为 Stream
Map<String,Integer> map = new HashMap<>();
map.put("a", 100);
map.put("b", 200);
Stream stream5 = map.entrySet().stream();

通过数组生成

1
2
int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);

该方法生成的流是数值流(IntStream)而不是Stream,使用数值流可以避免计算过程中拆箱装箱,提高性能

通过值生成

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

通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流

通过文件生成

1
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行

通过函数生成

  1. iterate
1
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);

iterate方法接受两个参数,第一个为初始值,第二个为进行的函数操作,因为iterate生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数

  1. generate
1
Stream<Double> stream = Stream.generate(Math::random).limit(5);

generate方法接受一个参数,方法参数类型为Supplier,由他为流提供值,generate生成的流也是无限流,因此通过limit对流进行了截断

中间操作

filter条件筛选

返回结果生成新的流中只包含满足筛选条件的数据

1
2
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

distinct去除重复元素

1
2
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();

limit返回指定流个数

1
2
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5); 
Stream<Integer> stream = integerList.stream().limit(3);

通过limit方法指定返回流的个数,limit的参数值必须>=0,否则将会抛出异常

skip跳过流中的元素

1
2
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);

通过skip方法跳过流中的元素,skip的参数值必须>=0,否则将会抛出异常

map流映射

流映射就是将接受的元素映射成另外一个元素,通过map方法可以完成映射

1
2
List<String> stringList = Arrays.asList("Java 8", "Lambdas",  "In", "Action");
Stream<Integer> stream = stringList.stream().map(String::length);
  1. 当出现相同的key时,解决方法:取前面value的值,或者取后面放入的value值,则覆盖先前的value值
1
2
3
4
Map<Long, String> map = userList.stream()
.collect(Collectors.toMap(User::getId, User::getUsername, (v1, v2) -> v1));
Map<Long, String> map = userList.stream()
.collect(Collectors.toMap(User::getId, User::getUsername, (v1, v2) -> v2));
  1. 对相同key值的数据进行合并
1
2
 // 处理itemList 合并相同物料累加数量
Map<Long, Integer> map = itemList.stream().collect(Collectors.toMap(StocksComponentsItem::getStocksId, StocksComponentsItem::getCount, (e1, e2) -> e1 + e2));
  1. 获取treeMap,根据key值进行排序
1
2
3
4
Map<Long, String> treeMap = new HashMap<>();
TreeMap<Long, String> map = treeMap.entrySet().stream()
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue(),
(v1, v2) -> v1, TreeMap::new));

常用方法

  • mapToDouble
  • mapToInt
  • mapToLong

flatMap流转换

扁平化映射,将多个stream连接成一个stream,这个操作是针对类似多维数组的,比如集合里面包含集合,相当于降维作用

例如:如果想要从List中取出学生列表,需要取出每个班级的学生List,再for循环调用List.addAll()方法把所有班级的学生List到一个新的总和List中

1
2
3
4
5
@Data
public class StdCls {
private String clsNo;
private List<Student> studentList;
}

使用map映射后会变成List<List>,若使用flatMap会进行扁平化处理,从而将List嵌套List用stream合并成一个List

1
2
3
4
List<String> nameList = stdClsList.stream()
.map(StdCls::getStudentList)
.flatMap(Collection::stream) // 多个集合List合并
.map(Student::getName).collect(Collectors.toList());

sorted排序

将流中的元素按照自然排序方式进行排序。

  1. sorted():自然排序,流中元素需实现Comparable接口
1
2
3
List<String> list = Arrays.asList("aa", "ff", "dd");
//String 类自身已实现Compareable接口
list.stream().sorted().forEach(System.out::println);// aa dd ff
  1. sorted(Comparator com):定制排序,自定义Comparator排序器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
Student s3 = new Student("aa", 30);
Student s4 = new Student("dd", 40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);

//自定义排序:先按姓名升序,姓名相同则按年龄升序
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); 

peek消费

对流中每个元素执行操作,并返回一个新的流,返回的流还是包含原来流中的元素。

如同于map,能得到流中的每一个元素,但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值

1
2
3
4
5
6
7
8
9
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
List<Student> studentList = Arrays.asList(s1, s2);

studentList.stream().peek(o -> o.setAge(100)).forEach(System.out::println);

//结果:
Student{name='aa', age=100}
Student{name='bb', age=100}

concat流合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();

list.add("心如音");
list.add("流老蛋");
list.add("王值");
list.add("李尔");
list.add("张新敏");
list.add("张天坤");

//需求1:取前4个数据组成一个流
Stream<String> s1 = list.stream().limit(4);

//需求2:跳过2个数据组成一个流
Stream<String> s2 = list.stream().skip(2);

//需求3:合并需求1和需求2得到的流,并把结果在控制台输出
Stream.concat(s1,s2).forEach(System.out::println);

//需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
Stream.concat(s1,s2).distinct().forEach(System.out::println);
}

终端操作

match元素匹配

  1. allMatch匹配所有
1
2
3
4
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
System.out.println("值都大于3");
}
  1. anyMatch匹配其中一个
1
2
3
4
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
System.out.println("存在大于3的值");
}
  1. noneMatch全部不匹配
1
2
3
4
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
System.out.println("值都小于3");
}

count统计流中元素个数

1
2
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();

find查找

  1. findFirst:查找第一个
  2. findAny:随机查找一个
1
2
3
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

min、max获取流中最小最大值

1
2
3
4
5
Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);

OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();

reduce元素组合

把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// reduce():字符串拼接
String reduceStr1 = Stream.of("ma", "zhi", "chu").reduce("", String::concat);
String reduceStr2 = Stream.of("ma", "zhi", "chu").reduce("", (x,y)->x+y);
System.out.println(reduceStr1);
System.out.println(reduceStr2);

// reduce():求和,identity(起始值)为0
Integer total1 = Stream.of(1,2,3,4).reduce(0, Integer::sum);
Integer total2 = Stream.of(1,2,3,4).reduce(0, (x, y) -> x +y);
System.out.println(total1);
System.out.println(total2);

// 求和,sumValue = 10, 无起始值
Integer total3 = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println(total3);

// reduce():求最小值
double minValue = Stream.of(-1.1, 8.8, -2.2, -6.6).reduce(Double.MAX_VALUE, Double::min);
System.out.println(minValue);

Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);

求值

  1. summingInt:求和
  2. sum:求和
  3. averagingInt:求平均值
  4. summarizingInt:同时求总和、平均值、最大值、最小值
1
2
3
4
5
6
7
8
9
10
11
int sum = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
int sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);
int sum = menu.stream().mapToInt(Dish::getCalories).sum();

double average = menu.stream().collect(Collectors.averagingInt(Dish::getCalories));

IntSummaryStatistics intSummaryStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
double average = intSummaryStatistics.getAverage(); //获取平均值
int min = intSummaryStatistics.getMin(); //获取最小值
int max = intSummaryStatistics.getMax(); //获取最大值
long sum = intSummaryStatistics.getSum(); //获取总和

foreach元素遍历

遍历流中的每一个元素,按照指定的方法执行,执行顺序不一定按照流的顺序

1
2
3
4
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
integerList.stream().forEach(System.out::println);
// foreach:遍历流中每一个元素,执行顺序不一定按照流的顺序,.parallel()表示创建一个并行流
Stream.of(1,2,3,4,5,6).parallel().forEach(System.out::println);

toArray返回数组

1
2
3
// toArray():将流中的元素放入到一个数组中
String[] strings = Stream.of("ma", "zhi", "chu").toArray(String[]::new);
System.out.println(Arrays.toString(strings));

collect返回集合

minBy/maxBy获取最小、最大值

1
2
Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(Collectors.minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(Collectors.maxBy(Integer::compareTo));

toMap获取属性映射

1
2
3
4
5
6
7
8
9
List<String> strings = menu.stream().map(Dish::getName).collect(Collectors.toList());
Set<String> sets = menu.stream().map(Dish::getName).collect(Collectors.toSet());

//获取属性和对象本身
Map<Integer, House> houseMap = houses.stream().collect(Collectors.toMap(House::getOwnerid, o -> o));
Map<Integer, House> houseMap1 = houses.stream().collect(Collectors.toMap(House::getOwnerid, Function.identity()));
//出现重复id时,取前面value的值,获取取后面放入的value值,则覆盖先前的value值
houses.stream().collect(Collectors.toMap(House::getOwnerid, House::getHousename,(v1,v2)->v2));
houses.stream().collect(Collectors.toMap(House::getOwnerid, House::getHousename,(v1,v2)->v1));

counting统计元素个数

1
2
3
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();
Long result = integerList.stream().collect(Collectors.counting());

joining拼接流中的元素

1
String result = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));

默认如果不通过map方法进行映射处理拼接的toString方法返回的字符串,joining的方法参数为元素的分界符,如果不指定生成的字符串将是一串的

groupingBy元素分组

1
2
3
4
5
Map<Type, List<Dish>> result = dishList.stream().collect(Collectors.groupingBy(Dish::getType));

// 分组再分组
Map<String, Map<Integer, List<Dish>>> result = menu.stream().collect(Collectors.groupingBy(Dish::getName,
Collectors.groupingBy(Dish::getCalories)));
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
// 源数据
ArrayList<GateScanCodeRecord> objects = new ArrayList<>();
objects.add(new GateScanCodeRecord().setMonth("2020-07").setDay("2020-07-12"));
objects.add(new GateScanCodeRecord().setMonth("2020-06").setDay("2020-06-14"));
objects.add(new GateScanCodeRecord().setMonth("2020-06").setDay("2020-06-12"));
objects.add(new GateScanCodeRecord().setMonth("2020-05").setDay("2020-05-17"));
objects.add(new GateScanCodeRecord().setMonth("2020-05").setDay("2020-05-12"));

// 分组有序
TreeMap<String, List<GateScanCodeRecord>> collect2 = objects.parallelStream().collect(Collectors.groupingBy(GateScanCodeRecord::getMonth, TreeMap::new, Collectors.toList()));

// 自定义分组有序
TreeMap<String, List<GateScanCodeRecord>> collect3 =
objects.parallelStream().collect(Collectors.groupingBy(GateScanCodeRecord::getMonth,
() -> new TreeMap<>((o1, o2) -> Math.toIntExact(Long.parseLong(o2.replaceAll("-","")) - Long.parseLong(o1.replaceAll("-","")))),
Collectors.toList()));

// 两层排序(按月分组排序,月下的数据按天分组并排序)
TreeMap<String, TreeMap<String, List<GateScanCodeRecord>>> collect = objects.stream()
.collect(Collectors.groupingBy(GateScanCodeRecord::getMonth,
() -> new TreeMap<>((o1, o2) -> Math.toIntExact(Long.parseLong(o2.replaceAll("-","")) - Long.parseLong(o1.replaceAll("-","")))),
Collectors.groupingBy(GateScanCodeRecord::getDay,
() -> new TreeMap<>((o1, o2) -> Math.toIntExact(Long.parseLong(o2.replaceAll("-","")) - Long.parseLong(o1.replaceAll("-","")))),
Collectors.toList()))
);

partitioningBy元素分区

1
2
3
4
5
6
Map<Boolean, List<Dish>> result = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));
//等同于
Map<Boolean, List<Dish>> result = menu.stream().collect(Collectors.groupingBy(Dish::isVegetarian));

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> result = integerList.stream().collect(Collectors.partitioningBy(i -> i < 3));

mapping获取属性映射集合

对分组之后的对象集合转换为对象的某个属性的集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
// 四个参与测试的小伙伴
Person tom = new Person("tom", "男", 11);
Person amy = new Person("amy", "女", 13);
Person ali = new Person("ali", "男", 12);
Person daming = new Person("daming", "男", 13);
personList.add(tom);
personList.add(amy);
personList.add(ali);
personList.add(daming);
// 对小伙伴按照性别age进行分组
Map<String, Set<String>> resultMap = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.mapping(Person::getName, Collectors.toSet())));
System.out.println(resultMap.toString());
}

Optional

  1. Optional.of(T value):通过一个非null的value来构造一个Optional,返回的Optional包含了value这个值,对于该方法,传入的参数一定不能为null,否则会抛出NullPointerException
  2. Optional.ofNullable(T value):与of的区别在于,传入的参数可以为null,进行三目运算,判断传入的参数是否为null,如果为null的话,返回的就是Optional.empty()
  3. Optional.empty():用来构造一个空的Optional,即该Optional中不包含值

ifPresent

如果Optional中有值,则对该值调用consumer.accept,否则什么也不做

1
2
Optional<User> user = Optional.ofNullable(getUserById(id));
user.ifPresent(u -> System.out.println("Username is: " + u.getUsername()));

orElse

如果Optional中有值则将其返回,否则返回orElse方法传入的参数

1
2
3
public T orElse(T other) {
return value != null ? value : other;
}
1
2
3
4
User user = Optional
.ofNullable(getUserById(id))
.orElse(new User(0, "Unknown"));
System.out.println("Username is: " + user.getUsername());

orElseGet

与orElse方法的区别在于:orElseGet方法传入的参数为一个Supplier接口的实现,当Optional中有值的时候,返回值;当Optional中没有值的时候,返回从该Supplier获得的值

1
2
3
public T orElseGet(Supplier<? extends T> ither) {
return value != null ? value : other.get();
}
1
2
3
User user = Optional.ofNullable(getUserById(id))
.orElseGet(() -> new User(0, "Unknown"));
System.out.println("Username is: " + user.getUsername());

orElseThrow

与orElse方法的区别在于:orElseThrow方法当Optional中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的exceptionSupplier提供

1
2
3
4
5
6
7
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
1
2
User user = Optional.ofNullable(getUserById(id))
.orElseThrow(() -> new EntityNotFoundException("id 为 " + id + " 的用户没有找到"));

map

如果当前Optional为Optional.empty,则依旧返回Optional.empty;否则返回一个新的Optional,该Optional包含的是:函数mapper在以value作为输入时的输出值

1
2
3
4
5
6
7
8
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()){
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
1
2
3
4
5
6
7
Optional<String> username = Optional
.ofNullable(getUserById(id))
.map(user -> user.getUsername())
.map(name -> name.toLowerCase())
.map(name -> name.replace('_', ' '));

System.out.println("Username is: " + username.orElse("Unknown"));

flatMap

与map方法的区别在于,map方法参数中的函数mapper输出的是值,然后map方法会使用Optional.ofNullable将其包装为Optional,而flatMap要求参数中的函数mapper输出的就是Optional

1
2
3
4
5
6
7
8
public <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()){
return empty();
} else {
return Objects.requireNonNull(mapper.apply(value));
}
}
1
2
3
4
5
6
Optional<String> username = Optional
.ofNullable(getUserById(id))
.flatMap(user -> Optional.of(user.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));

System.out.println("Username is: " + username.orElse("Unknown"));

filter

filter方法接受一个Predicate来对Optional中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个Optional,否则返回Optional.empty

1
2
3
4
5
6
7
8
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if(!isPresent()) {
return this;
} else {
return predicate.test(value) ? this : empty();
}
}
1
2
3
4
5
6
Optional<String> username = Optional
.ofNullable(getUserById(id))
.filter(user -> user.getId() < 10)
.map(user -> user.getUsername());

System.out.println("Username is: " + username.orElse("Unknown"));