stream流使用

  stream流类似于lambda表达式的函数式编程,集合使用过滤、添加元素等更加方便。

根据Collection获取流

  • Collection接口中有一个stream()方法,可以获取流 , default Stream<E> stream():获取一个Stream流

    1. 通过List集合获取:           list.stream()

    2. 通过Set集合获取

根据Map获取流

  • 使用所有键的集合来获取流

  • 使用所有值的集合来获取流

  • 使用所有键值对的集合来获取流

list.stream().map(p -> map.get(p.getType())).collect(Collectors.toList());

根据数组获取流

  • Stream流中有一个static <T> Stream<T> of(T... values)

    • 通过数组获取:             (Stream.of(arr))

    • 通过直接给多个数据的方式

函数拼接与终结方法

在上述介绍的各种方法中,凡是返回值仍然为Stream接口的为函数拼接方法,它们支持链式调用;而返回值不再为Stream接口的为终结方法,不再支持链式调用。如下表所示:

方法名方法作用方法种类是否支持链式调用

说明

count 统计个数 终结  
forEach 逐一处理 终结  
filter 过滤 函数拼接  
limit 取用前几个 函数拼接  
skip 跳过前几个 函数拼接  
map 映射 函数拼接

流中的元素映射到另外一个流中

(转换为其他类型)

concat 组合 函数拼接 拼接两个流

收集Stream结果

1、收集到集合中

<R,A> R collect(Collector<? super T,A,R> collector): 把流中的数据收集到单列集合中

参数Collector如何得到? 使用java.util.stream.Collectors工具类中的静态方法:

  • public static <T> Collector<T, ?, List<T>> toList():转换为List集合。

  • public static <T> Collector<T, ?, Set<T>> toSet():转换为Set集合。

示例:stream.collect(collectors.toList())

   stream.collect(collectors.toSet())

2、收集到数组中

Stream提供toArray方法来将结果放到一个数组中,返回值类型是Object[]的:

Object[] toArray();

Java中双冒号::的使用

使用范例

方法调用

person -> person.getAge();
可以替换成
Person::getAge

x -> System.out.println(x)
可以替换成
System.out::println
out是一个PrintStream类的对象,println是该类的方法,依据x的类型来重载方法

创建对象

() -> new ArrayList<>();
可以替换为
ArrayList::new
new关键字实际上调用的是ArrayList的构造方法

----------------------------------更新-------------------------------------------

flapmap使用

参考https://leetcode-cn.com/problems/flatten-nested-list-iterator/    

341. 扁平化嵌套列表迭代器

扁平化,拆解外层的option

public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
}

  

List<String> words = new ArrayList<>();
words.add("Hello");
words.add("World");
List<String> uniqueCharacters =
words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());

  

List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<Integer> numbers2 = Arrays.asList(3, 4);
        List<int[]> pairs = numbers1.stream()
                .flatMap(i -> numbers2.stream()
                        .map(j -> new int[] {i, j})
                )
                .collect(toList());

  输出[[1,3],[1,4],[2,3],[2,4],[3,3],[3,4]]

查找元素

menu.stream()
.filter(Dish::isVegetarian)
.findAny()
.ifPresent(d -> System.out.println(d.getName());

  

reduce 归约

int sum = numbers.stream().reduce(0, (a, b) -> a + b);

int sum = numbers.stream().reduce(0, Integer::sum);

Optional<Integer> max = numbers.stream().reduce(Integer::max);

分组groupingBy

public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return
CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
} ));

//多级分组
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
menu.stream().collect(
groupingBy(Dish::getType,
groupingBy(dish -> {
if (dish.getCalories() <= 400) {
return CaloricLevel.DIET;
} else if (dish.getCalories() <= 700) {
return CaloricLevel.NORMAL;
} else {
return CaloricLevel.FAT;
}
})
)
);

  

 分区partitioningBy

Map<Boolean, List<Dish>> partitionedMenu =
menu.stream().collect(partitioningBy(Dish::isVegetarian));
这会返回下面的Map:
{false=[pork, beef, chicken, prawns, salmon],
true=[french fries, rice, season fruit, pizza]}

partitioningBy工厂方法有一个重载版本,可以像下面这样传递第二个收集器:
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));
这将产生一个二级Map:
{false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]},true={OTHER=[french fries, rice, season fruit, pizza]}}

这里,对于分区产生的素食和非素食子流,分别按类型对菜肴分组,得到了一个二级Map,
二级分组得到的结果类似。再举一个例子,你可以重用前面的代码来找到素食和非素
食中热量最高的菜:
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));
这将产生以下结果:
{false=pork, true=pizza}

  

optional对象

我们决定采用orElse方法读取这个变量的值,使用这种方式你还可以定义一个默认值,遭
遇空的Optional变量时,默认值会作为该方法的调用返回值。Optional类提供了多种方法读取
Optional实例中的变量值。
 get()是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量
值,否则就抛出一个NoSuchElementException异常。所以,除非你非常确定Optional
变量一定包含值,否则使用这个方法是个相当糟糕的主意。此外,这种方式即便相对于
嵌套式的null检查,也并未体现出多大的改进。
 orElse(T other)是我们在代码清单10-5中使用的方法,正如之前提到的,它允许你在
Optional对象不包含值时提供一个默认值。
 orElseGet(Supplier<? extends T> other)是orElse方法的延迟调用版,Supplier
方法只有在Optional对象不含值时才执行调用。如果创建默认值是件耗时费力的工作,
你应该考虑采用这种方式(借此提升程序的性能),或者你需要非常确定某个方法仅在
Optional为空时才进行调用,也可以考虑该方式(这种情况有严格的限制条件)。
 orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法非常类似,
它们遭遇Optional对象为空时都会抛出一个异常,但是使用orElseThrow你可以定制希
望抛出的异常类型。
 ifPresent(Consumer<? super T>)让你能在变量值存在时执行一个作为参数传入的
方法,否则就不进行任何操作。
Optional类和Stream接口的相似之处,远不止map和flatMap这两个方法

原文地址:https://www.cnblogs.com/chenfx/p/14127990.html