java8知识点总结

一、java8的四类函数式接口

Consumer<T> 消费型

void accept(T t); 接收一个输入参数,无返回值。

Supplier<T> 供给型

T get(); 无参数,返回一个结果。

Function<T,R> 文档型

R apply(T t); 接收一个输入参数,返回一个结果。

Predicate<T> 断言型

boolean test(T t); 接收一个输入参数,返回一个布尔值结果。

二、Stream创建流的几种方式

空Stream(Empty Stream)

方法empty()被用于创建一个Empty Stream:

Stream<String> streamEmpty = Stream.empty();

上述代码段创建的Empty Stream通常被用于避免null对象或零元素对象的streams(streams
with no element)返回结果为null。

public Stream<String> streamOf(List<String> list){
  return list == null || list.isEmpty() ? Stream.empty() : list.streams();
}

集合Steram(Stream of Collection)

可以创建任意Collection接口衍生类(Collection->List、Set、Queue)的Streams:

Collections<String> collection = Arrays.asList("a", "b", "c");
Stream<Stirng> streamOfCollection = collection.stream();

数组Stream(Stream of Array)

创建数组Stream:

Stream<String> streamOfArray = Stream.of("a", "b", "c");

可以选择Stream中包含的元素数量:

String[] arr = new String[]{"a", "b", "c"};
Stream<String> streamOfArrayFull = Arrays.stream(arr);
Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3);
备注:截取的是(1,3]  ---->  输出的是b和c

构建器(Stream.builder())

当builder被用于指定参数类型时,应被额外标识在声明右侧,否则方法build()将创建一个Stream(Object)实例:

Stream<String> streamBuilder = Stream.<String>builder()
        .add("a").add("b").add("c")
        .build();

生成器(Stream.generator())

方法generator()接受一个供应器Supplier<T>用于元素生成。由于生产流(resulting
stream)被定义之后属于无限流(即无止境地不断生产),开发者必须指定stream拥有流的目标大小,否则方法generator()将持续生产直到jvm内存到达顶值(memory limit):

Stream<String> streamOfGenerated = Stream.generate( () -> "element").limit(10);

上述代码将创建十个内容为“element”的生成流。

迭代器(Stream.iterate())

另一种创建无限流的方法是通过调用方法iterate(),同样的它也需要使用方法limit()对目标流的元素大小进行限制:

Stream<Integer> streamItreated = Stream.iterate(40, n -> n + 2).limit(20);

迭代流即采用迭代的方法作为元素生产方式,类似于高中数学中的f(x),f(f(x)),etc。上述例子中,生成流的第一个元素是迭代器iterate()中的第一个元素40,从第二个元素开始的每个新元素都与上个元素有关,在此例中,生成流中的元素为:40、42、44 ... 76、78。

基元流(Stream of Primitives)

Java8提供了创建三大基础数据类型(int、long、double)stream的方式。由于Stream<T>是一个类接口,我们无法采用泛型传参的方式声明基础数据类型的stream,因此三个特殊的接口就被创造出来了:IntStream、LongStream、DoubleStream。使用它们能够避免不必要的自动装箱,以提高生产效率。

①IntStream

IntStream intStream = IntStream.range(1, 3);

方法range(int startInclusive, int
endInclusive)创建了一个有序流(从startInclusive到endInclusive)。它使后面的值每个增加1,但却不包括最后一个参数,即[),最终结果是1,2。

②LongStream

LongStream longStream = LongStream.rangeClosed(5, 300);

方法rangeClosed(int startInclusive, int endInclusive)与range()大致相同,它包括最后一个参数,即(),最终结果是5,6,7.....299,300

③DoubleStream

Random random = new Random();
DoubleStream doubleStream = random.doubles(3);

Java8之后,类Random也提供了拓展方法用于生成基础数据类型的stream,上述代码创建了一个含有3个随机数的DoubleStream。

字符串流(Stream of String)

String类型也可以作为生成stream的源,这得益于方法chars()的帮助,此外由于JDK中没有CharStream接口,IntStream也被用来表示字符流(stream
of chars)

IntStream streamOfChars = "2a".chars();

下例中通过正则表达式将a,b,c截取为字符串流。

Stream<String> streamOfString = Pattern.compile(",").spitAsStream("a,b,c");

文件流(Stream of File)

Java NIO类文件允许通过方法lines()生成文本文件的Stream<String>,文本的每一行都会变成stream的一个元素:

Path path = Paths.get("C:\file.txt");
Stream<String> streamOfString = Files.lines(path);
Stream<String> streamWithCharset = Files.lines(path, Charset.forName("utf-8"));

在方法lines()中也可以通过Charset设置文件编码。

引用Stream(Referencing a Stream)

只要调用生成操作(中间操作)就会实例化一个stream并生成一个可获取的引用,但执行终端操作会使得stream无法访问。

以下代码如果不考虑冗长的话将是有效的:

Stream<String> stream = Stream.of("a", "b", "c")
                .filter(t -> t.contains("b"));
Optional<String> anyElement = stream.findAny();

但是倘若我们在执行终端操作后重新使用相同的引用,则会不可避免的触发IllegalStateException。

Optional<String> firstElement = stream.findFirst();

IllegalStateException是一个运行时异常(RuntimeException),即编译器将不会提示此错误。因此必须记得JAVA8不允许重复使用stream。因为stream从设计上是用于操作数据源(集合、数组)所生成的元素序列,而不是存储元素。

所以想让上面的代码正常工作就得改为(List可以存储元素):

List<String> elements = Stream.of("a", "b", "c")
.filter(t -> t.contains("b"))
            .collect(Collectors.toList());
Optional<String> anyElement = elements.stream().findAny();
Optional<String> firstElement = elements.stream().findFirst();

三、中间操作

Filter(过滤)

过滤流中的某些元素

Stream<T> filter(Predicate<? super T> predicate);

案例

String[] dd = { "a", "b", "c" };
Stream<String> stream = Arrays.stream(dd);
stream.filter(str -> str.equals("a"))
      .forEach(System.out::println);//返回字符串为a的值

Map(映射)

接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

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

案例

//利用map把int转string
Integer[] dd = {1, 2, 3};
Stream<Integer> stream = Arrays.stream(dd);
stream.map(str -> Integer.toString(str))
        .forEach(str -> {
            System.out.println(str);//1,2,3
        });

//利用map把对象里的name参数提取成一个List<String>
List<Emp> list = Arrays.asList(new Emp("a"), new Emp("b"));
List<String> list1 = list.stream()
.map(Emp::getName)
.collect(Collectors.toList());

flatMap

接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

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

案例

List<String> list = Arrays.asList("a,b,c", "1,2,3");
Stream<String> newStream = list.stream().flatMap(s -> {
     String[] split = s.split(",");
     return Arrays.stream(split);
});
newStream.forEach(System.out::print);//abc123

distinct,sorted,peek,limit,skip

//去重
Stream<T> distinct();

//排序
Stream<T> sorted();

//定制排序,自定义Comparator排序器
Stream<T> sorted(Comparator<? super T> comparator);

//如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
Stream<T> peek(Consumer<? super T> action);

//获取maxSize个元素
Stream<T> limit(long maxSize);

//跳过n个元素
Stream<T> skip(long n);

案例

List<Emp> empList = new ArrayList<>();
empList.add(new Emp("张三", 45, 9000.0));
empList.add(new Emp("李四", 55, 10000.0));
empList.add(new Emp("王五", 20, 1500.0));
empList.add(new Emp("赵六", 25, 2500.0));
empList.add(new Emp("田七", 30, 4000.0));

//获取3个年龄>25岁的员工信息,根据工资排序
List<Emp> emps1 = empList.stream().filter(t -> t.getAge() > 25)
        .sorted(Comparator.comparing(Emp::getSalary))
        .distinct()
        .limit(3)
        .collect(Collectors.toList());
emps1.forEach(System.out::println);


//把年龄>30岁的员工工资 x 1.5倍
List<Emp> emps2 = empList.stream().filter(t -> t.getAge() > 30)
        .peek(emp -> {
            emp.setSalary(emp.getSalary() * 1.5);
        })
        .collect(Collectors.toList());
emps2.forEach(System.out::println);


//数字从1开始迭代,跳过前5个数再往后获取10个数
List<Integer> nums = Stream.iterate(1, i -> i + 1)
        .skip(5)
        .limit(10)
        .collect(Collectors.toList());
nums.forEach(System.out::println);//输出6,7,8.....14,15

四、终端操作

forEachOrdered和forEach

forEachOrdered按指定的顺序处理流元素,而不管流是顺序的还是并行的。

forEach在并行流中不仅以非确定顺序执行,还可以在不同线程中针对不同元素同时执行。

void forEach(Consumer<? super T> action);  
void forEachOrdered(Consumer<? super T> action);

案例

List<String> list = Arrays.asList("a", "b", "c");
//顺序流时都是按顺序操作
list.stream().forEachOrdered(System.out::print);//abc
list.stream().forEach(System.out::print);//abc

//并行流时forEachOrdered能按顺序操作,而forEach不能
list.parallelStream().forEachOrdered(System.out::print);//abc
list.parallelStream().forEach(System.out::print);//不确定

toArray

public final <A> A[] toArray(IntFunction<A[]> generator)

案例

List<String> strs = Arrays.asList("a", "b", "c");
String[] str1 = strs.stream().toArray(str -> new String[strs.size()]);
String[] str2 = strs.stream().toArray(String[]::new);
String[] str3 = strs.toArray(new String[strs.size()]);

Object[] obj1 = strs.stream().toArray();
Object[] obj2 = strs.toArray();

count,max,min

//返回流中元素的总个数
long count();

//返回流中元素最大值  
Optional<T> max(Comparator<? super T> comparator);

//返回流中元素最小值
Optional<T> min(Comparator<? super T> comparator);

findFirst,findAny

//返回流中第一个元素
Optional<T> findFirst();

//返回流中的任意元素
Optional<T> findAny();

案例

List<String> list = Arrays.asList("a", "b", "c", "d");

String findFirst = list.stream().findFirst().get();
String findAny = list.stream().findAny().get();

anyMatch,allMatch,noneMatch

//只要流中有一个元素满足该断言则返回true
boolean anyMatch(Predicate<? super T> predicate);  

//当流中每个元素都符合该断言时才返回true
boolean allMatch(Predicate<? super T> predicate); 

//当流中每个元素都不符合该断言时才返回true
boolean noneMatch(Predicate<? super T> predicate);

案例

List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
boolean aa = strs.stream()
        .anyMatch(str -> str.equals("a"));//true

boolean bb = strs.stream()
        .allMatch(str -> str.equals("a"));//false

boolean cc = strs.stream()
        .noneMatch(str -> str.equals("a"));//false

reduce

reduce
是一种归约操作,将流归约成一个值的操作叫做归约操作,用函数式编程语言的术语来说,这种称为折叠(fold)

①第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。

Optional<T> reduce(BinaryOperator<T> accumulator);

案例

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce((x1, x2) -> x1 + x2).get();
   等同于      list.stream().reduce(Integer::sum).get();
System.out.println(sum); // 55

②流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。

T reduce(T identity, BinaryOperator<T> accumulator);

案例

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(10, (x1, x2) -> x1 + x2);
System.out.println(sum); //65

③在顺序流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。

在并行流(parallelStream)中,我们知道流被fork
join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

顺序流案例:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Integer i1 = list.stream().reduce(0,
        (a1, a2) -> {
            System.out.println("stream accumulator: a1:" + a1 + " a2:" + a2);
            return a1 - a2;
        },
        (a1, a2) -> {
            System.out.println("stream combiner: b1:" + a1 + " b2:" + a2);
            return a1 * a2;
        });//顺序流中,粉色部分不会执行
System.out.println(i1); //-55

并行流案例

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Integer i2 = list.parallelStream().reduce(0,
        (a1, a2) -> {
            System.out.println("parallelStream accumulator: a1:" + a1 + " a2:" + a2);
            return a1 - a2;
        },
        (a1, a2) -> {
            System.out.println("parallelStream combiner: b1:" + a1 + " b2:" + a2);
            return a1 * a2;
        });//并行流中,粉色部分会执行
System.out.println(i2); //3628800

collect

<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);

<R, A> R collect(Collector<? super T, A, R> collector);

案例

List<Emp> list = new ArrayList<>();
list.add(new Emp("张三", 45, 9000.1));
list.add(new Emp("李四", 55, 10000.0));
list.add(new Emp("王五", 20, 1500.0));
list.add(new Emp("赵六", 25, 2550.0));
list.add(new Emp("田七", 30, 4100.0));

// 转list
List<String> names1 = list.stream().map(Emp::getName).collect(Collectors.toList());

// 转set
Set<String> names2 = list.stream().map(Emp::getName).collect(Collectors.toSet());

// 转map,需要指定key和value,Function.identity()表示当前的Emp对象本身
Map<String, Emp> map = list.stream().collect(Collectors.toMap(Emp::getName, Function.identity()));

// 计算元素中的个数
Long count = list.stream().collect(Collectors.counting());
Long count1 = (long) list.size();

// 数据求和 summingInt summingLong,summingDouble
Integer sumAges = list.stream().collect(Collectors.summingInt(Emp::getAge));

// 平均值 averagingInt,averagingLong,averagingDouble
Integer aveAge = list.stream().collect(Collectors.averagingInt(Emp::getAge)).intValue();
Double aveSalary = list.stream().collect(Collectors.averagingDouble(Emp::getSalary));

// 综合处理类(包含count,sum,min,max,average)
// summarizingInt,summarizingLong,summarizingDouble
IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(Emp::getAge));

// 连接字符串,可以使用重载的方法,加上一些前缀,后缀和中间分隔符
String strEmp1 = list.stream().map(Emp::getName).collect(Collectors.joining(","));
//张三,李四,王五,赵六
String strEmp2 = list.stream().map(Emp::getName).collect(Collectors.joining("-中间的分隔符-", "最前缀*", "&最后缀"));

// maxBy 按照比较器中的比较结果刷选最大值
Optional<Integer> maxAge = list.stream().map(Emp::getAge)
        .collect(Collectors.maxBy(Comparator.comparing(Function.identity())));

// 最小值
Optional<Integer> minAge = list.stream().map(Emp::getAge)
        .collect(Collectors.minBy(Comparator.comparing(Function.identity())));
System.out.println("max:" + maxAge);
System.out.println("min:" + minAge);

// 归约操作
list.stream().map(Emp::getAge).collect(Collectors.reducing((x, y) -> x + y));
list.stream().map(Emp::getAge).collect(Collectors.reducing(0, (x, y) -> x + y));

// 分操作 groupingBy 根据地址,把原list进行分组
Map<Integer, List<Emp>> mapGroup = list.stream().collect(Collectors.groupingBy(Emp::getAge));

// partitioningBy 分区操作 需要根据类型指定判断分区
Map<Boolean, List<Integer>> partitioningMap = list.stream().map(emp -> emp.getAge())
        .collect(Collectors.partitioningBy(emp -> emp > 20));

五、其他

Optional静态类

在java8中,很多的stream的终端操作,都返回了一个Optional<T>对象,这个对象,是用来解决空指针的问题,而产生的一个类。

private Optional() {
    this.value = null;
}

private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}

public static <T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);

1.get()直接取,如果为null,就返回异常。

2.orElse(T
other)在取这个对象的时候,设置一个默认对象(默认值);如果当前对象为null的时候,就返回默认对象。

3.orElseGet(Supplier<? extends T>
other)跟第二个是一样的,区别只是参数,传入了一个函数式参数。

4.orElseThrow(Supplier<? extends X>
exceptionSupplier)第四个,跟上面表达的是一样的,为null的时候,返回一个特定的异常。

5. isPresent()是对当前的value进行null判断。

案例

Emp emp = new Emp("张三", "上海", "22");

Optional<Emp> op = Optional.ofNullable(emp);
System.out.println(op.get().getAddress());//上海

Optional<Emp> op1 = Optional.ofNullable(null);
System.out.println(op1.orElse(emp).getAddress());//上海

//这里指定了一个默认对象emp,为先创建的一个emp对象,emp对象里的成员变量还没有赋值,所以输出为null
System.out.println(op1.orElseGet(Emp::new).getAddress());

// java.lang.RuntimeException
try {
    System.out.println(op1.orElseThrow(RuntimeException::new));
} catch (Exception e) {
    e.printStackTrace();
}
原文地址:https://www.cnblogs.com/supiaopiao/p/15079223.html