Java8 新特性学习总结

目录

Lambda表达式初步与函数式接口

深入函数式接口与方法引用

Lambda表达式深入与流初步

Function与BiFunction函数式接口详解

BiFunction函数式接口实例演示

Predicate函数式接口详解

Predicate深入剖析与函数式编程本质

Supplier与函数式接口总结

Optional深入详解

方法引用详解

方法引用场景剖析与默认方法分析

Stream介绍与操作方式详解

Stream深度解析与源码实践

Stream实例剖析

Stream陷阱剖析

内部迭代与外部迭代本质剖析及流本源分析

流的短路与并发流

Stream分组与分区详解

Collector源码分析与收集器核心

Collector同一性与结合性分析

Collector复合与注意事项

收集器用法详解与多级分组和分区

比较器详解与类型推断特例

自定义收集器实现

自定义收集器深度剖析与并行流陷阱

收集器枚举特性深度解析与并行流原理

Collectors工厂类源码分析与实战


  • Lambda表达式初步与函数式接口

初探:

public class SwingTest {
    public static void main(String[] args) {
        JFrame jFrame = new JFrame("My JFream");
        JButton jButton = new JButton("My JButton");
        jButton.addActionListener(event -> System.out.println("BT"));


        jFrame.add(jButton);
        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Lambda表达式的基本结构:

(param1、param2、param3) -> {执行体}

 

JDK8新增的包:package java.util.function;

关于函数式接口:

1、如果一个接口只有一个抽象方法,那么该接口就是一个函数式结构;

2、如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口;

3、如果某个接口只有一个抽象方法,但我们并没有给该接口声明@FunctionalInterface 注解,那么编译器依旧会将该接口看作是函数式接口。

public class CollectTest {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,9);
        /**
         *  default void forEach(Consumer<? super T> action) {
         *         Objects.requireNonNull(action);
         *         for (T t : this) {
         *             action.accept(t);
         *         }
         *  }
         *
         * Performs the given action for each element 
         * of the {@code Iterable} until all elements 
         * have been processed or the action throws an
         * exception.  Unless otherwise specified by 
         * the implementing class,actions are performed 
         * in the order of iteration (if an iteration order
         * is specified).  
         * Exceptions thrown by the action are relayed to the
         * caller.
         */
        /**
         *  Represents an operation that accepts 
         *  a single input argument and returns no
         *  result. Unlike most other functional interfaces, 
         *  {@code Consumer} is expected
         *  to operate via side-effects.
         */
        list.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
        
        //-------------lambda表达式形式-----------
        list.forEach(i ->{
            System.out.println(i);
        });
        //通过方法引用的形式实现
        list.forEach(System.out::println);
    }
}

 

  • 深入函数式接口与方法引用

关于自定义抽象接口:

一个接口中只能有一个抽象方法;

接口中可以定义default类型是具体方法,有实现功能,是默认方法。

Note that instances of functional interfaces can be created with

lambda expressions, method references, or constructor references.

函数式接口的实现可以通过三种方式来实现:

lambda表达式、方法引用、构造方法引用。

 

Lambda表达式作用

Lambda表达式为Java添加了缺失的函数式编程特性,使我们能将函数当作一等公民看待;

在将函数作为一等公民的语言中,Lambda表达式类型式函数,但是在Java中,Lambda表达式式对象,他们必须依附于一类特别的对象类型---函数式接口(Functional Interface)

 

外部迭代:我们平时使用的正常的foreach方法。

内部迭代:直接使用集合内部的一个函数式接口来遍历集合中的数据,类似上面例子中的Consumer接口,实现接口中的accept方法来遍历处理集合中的数据。

 

 

  • Lambda表达式深入与流初步

public class Test3 {
    public static void main(String[] args) {
        OjbectInterface oi1 = () -> {};
        System.out.println(oi1.getClass().getInterfaces()[0]);


        OjbectInterface oi2 = () -> {};
        System.out.println(oi2.getClass().getInterfaces()[0]);
        
    }
}


@FunctionalInterface
interface OjbectInterface{
    void objectMethod();
}


@FunctionalInterface
interface OjbectInterface2{
    void objectMethod();
}

Lambda表达式必须通过上下文来确定类型的,如果没有上下文,根本不知道表示的什么类型。

 

流的形式:中间流和节点流

public class Test3 {
    public static void main(String[] args) {
        //证明Lambda表达式在Java中是对象
        OjbectInterface oi1 = () -> {};
        System.out.println(oi1.getClass().getInterfaces()[0]);
        OjbectInterface oi2 = () -> {};
        System.out.println(oi2.getClass().getInterfaces()[0]);
        //创建线程
        new Thread(() -> System.out.println("helloworld")).start();
        List<String> list = Arrays.asList("hello","java","scala");
        List<String> aimList = new ArrayList<>();
        list.forEach(item -> aimList.add(item.toUpperCase()));
        aimList.forEach(item -> {System.out.println(item);});


        //更简单的操作stream方法
        list.stream().map(item -> item.toUpperCase())
            .forEach(item -> System.out.println(item));
        list.stream().map(String::toUpperCase)
            .forEach(System.out::println);


    }
}


@FunctionalInterface
interface OjbectInterface{
    void objectMethod();
}


@FunctionalInterface
interface OjbectInterface2{
    void objectMethod();
}

 

  • Function接口详解
Represents a function that accepts one argument and produces a result.

函数式接口中可以定义静态方法。

//第一个参数是调用toUpdateCase这个方法的对象。
Function<String,String> function = String::toUpperCase;


public class StringCompapator {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("node","java","scala");
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });
        Collections.sort(list,Comparator.reverseOrder());
        Collections.sort(list,(String o1,String o2) ->{
            return o2.compareTo(o1);
        });
        Collections.sort(list,(String o1,String o2) -> 
                        o2.compareTo(o1));
        System.out.println(list);
    }
}

Java Lambda概要

Java Lambda表达式是一种匿名函数;它是没有声明的方法,即没有访问修饰符、返回值声明和名字。

 

Lambda表达式作用:

传递行为,而不仅仅是值;

提升抽象层次;

API重用性更好;

更加灵活。

 

Java Lambda表达式的基本语法:

Java中的Lambda表达式基本语法:(argument) -> (body)

比如说:

(arg1,arg2...) -> {body}

(type1 arg1,type2 arg2...) -> {body}

 

Java Lambda表达式结构:

一个Lambda表达式可以有零个或多个参数;

参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同。

所有参数需要包含在圆括号内,参数之间用逗号相隔。例如:(a,b)或(int a,int b)或(String a,int b,float c);

空圆括号代表参数集是空。例如:() -> 42;

只有一个参数,且类型可推导时,圆括号可以省略。

如果Lambda表达式的主体有一条语句,花括号可以省略,如果是多条,则需要花括号包含。

public class FunctionTest {
    public static void main(String[] args) {
        FunctionTest functionTest = new FunctionTest();
        //传递行为
        //statement
        System.out.println(functionTest.compute(1,v->{return 2*v;}));
        //expression
        System.out.println(functionTest.compute(2,v -> 5 + v));
    }


    public int compute(int a, Function<Integer,Integer> function){
        int result = function.apply(a);
        return result;
    }
}

高阶函数:如果一个函数接收一个函数作为参数,或者返回一个函数作为返回值,那么该函数就叫做高阶函数。(函数中不知道要完成的是什么操作,根据传入的函数确定。

 

  • Function与BiFunction函数式接口详解

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

先执行输入函数的apply方法,然后把返回结果作为当前函数的输入参数,再执行当前函数方法

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

先使用当前函数的方法,然后将返回结果作为传入函数方法的入参,执行传入函数的方法。

public class FunctionTest2 {
    public static void main(String[] args) {
        FunctionTest2 test = new FunctionTest2();
        //12
        //System.out.println(test.compute(2,v->v*3,v->v*v));
        //36
        System.out.println(test.compute2(2,v->v*3,v->v*v));
        //-3
        System.out.println(test.compute3(2,5,(v1,v2)->v1-v2));
        //9
        System.out.println(test.compute4(2,5,(v1,v2)->v1-v2,v->v*v));


    }


    public int compute(int a, Function<Integer,Integer> function1, Function<Integer,Integer> function2){
        return function1.compose(function2).apply(a);
    }
    public int compute2(int a, Function<Integer,Integer> function1, Function<Integer,Integer> function2){
        return function1.andThen(function2).apply(a);
    }


    public int compute3(int a, int b, BiFunction<Integer,Integer,Integer> biFunction){
        return biFunction.apply(a,b);
    }


    public int compute4(int a, int b, BiFunction<Integer,Integer,Integer> biFunction,Function<Integer,Integer> function){
        return biFunction.andThen(function).apply(a,b);
}

BiFunction:Represents a function that accepts two arguments and produces a result.

 

  • BiFunction函数式接口实例演示

public class PersonTest {
    public static void main(String[] args) {
        Person person1 = new Person(20,"xiaohong");
        Person person2 = new Person(30,"xiaoming");
        Person person3 = new Person(50,"xiaohong");
        Person person4 = new Person(20,"xiaogang");


        List<Person> persons = Arrays.asList(person1,person2,person3);
        PersonTest personTest = new PersonTest();
        
        personTest.getPersonByUserName("xiaohong",persons)
        .forEach(person -> System.out.println(person.getAge()));
        
        personTest.getPersonByAge(20,persons)
        .forEach(person -> System.out.println(person.getUserName()));
        
        personTest.getPersonByAge2(20,persons,(age,personList)->
        personList.stream().filter(person -> person.getAge() > age)
        .collect(Collectors.toList()))
        .forEach(person -> System.out.println(person.getUserName()));
    }


    public List<Person> getPersonByUserName(String userName,List<Person> persons){
        return persons.stream().filter(person -> 
        person.getUserName().equals(userName))
        .collect(Collectors.toList());
    }


    public List<Person> getPersonByAge(int age,List<Person> persons){
        BiFunction<Integer,List<Person>,List<Person>> biFunction = 
        (personAge,personList)->personList.stream()
        .filter(person -> person.getAge()>personAge)
        .collect(Collectors.toList());
        return biFunction.apply(age,persons);
    }


    public List<Person> getPersonByAge2(int age,List<Person> persons,
           BiFunction<Integer,List<Person>,List<Person>> biFunction) {
        return biFunction.apply(age,persons);
    }
}

 

  • Predicate函数式接口详解

public class PredicateTest {
    public static void main(String[] args) {
       Predicate<String> predicate = p -> p.length() > 5;
       System.out.println(predicate.test("helloMoto"));
    }
}

 

  • Predicate深入剖析与函数式编程本质

public class PredicateTest2 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        PredicateTest2 predicate = new PredicateTest2();
        //predicate.conditionFilter(list,item -> item%2==0);
        predicate.andCondition(list,item->item%2==0,item->item>5);
        predicate.orCondition(list,item->item%2==0,item->item>5);
        predicate.negateCondition(list,item->item%2==0,item->item>5);
    }


    public void conditionFilter(List<Integer> list,Predicate<Integer> predicate){
        list.stream().filter(item -> predicate.test(item))
            .forEach(System.out::println);
    }


    /**
     * 逻辑与
     * @param list
     * @param p1
     * @param p2
     */
    public void andCondition(List<Integer> list,Predicate<Integer> p1,Predicate<Integer> p2){
        list.stream()
            .filter(item -> p1.and(p2).test(item))
            .forEach(System.out::println);
    }


    /**
     * 逻辑或
     * @param list
     * @param p1
     * @param p2
     */
    public void orCondition(List<Integer> list,Predicate<Integer> p1,Predicate<Integer> p2){
        list.stream().filter(item -> p1.or(p2).test(item))
            .forEach(System.out::println);
    }


    /**
     * 取反
     * @param list
     * @param p1
     * @param p2
     */
    public void negateCondition(List<Integer> list,Predicate<Integer> p1, Predicate<Integer> p2){
        list.stream().filter(item -> p1.and(p2).negate().test(item))
            .forEach(System.out::println);
    }
}
  • Supplier与函数式接口总结

Represents a supplier of results.

There is no requirement that a new or distinct result be returned each time the supplier is invoked.

public class SupplierTest {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> "hello world";
        System.out.println(supplier.get());
        Supplier<Student> sutdent = () -> new Student();
        System.out.println(sutdent.get().getName());
        Supplier<Student> sutdent2 = Student::new;
        System.out.println(sutdent2.get().getName());
    }
}

BinaryOperator

Represents an operation upon two operands of the same type, producing a result of the same type as the operands.

public class BinaryOperatorTest {
    public static void main(String[] args) {
        BinaryOperatorTest bot = new BinaryOperatorTest();
        System.out.println(bot.compute(2,3,(a,b)->a+b));


        System.out.println(bot.getShort("hello123","bonru",
            (a,b)->a.length()-b.length()));
        System.out.println(bot.getShort("hello123","salu",
            (a,b)->a.charAt(0) - b.charAt(0)));
    }


    public int compute(int a,int b,BinaryOperator<Integer> binaryOperator){
        return binaryOperator.apply(a,b);
    }


    public String getShort(String a,String b,Comparator<String> comparable){
        return BinaryOperator.maxBy(comparable).apply(a,b);
    }
}
  • Optional深入详解

主要解决Java中NPE异常.

A container object which may or may not contain a non-null value.

public class OptionalTest {
    public static void main(String[] args) {
        Optional<String> opt1 = Optional.of("hello");
        //不能确认是不是null
        Optional<String> opt2 = Optional.ofNullable("hello");
        Optional<String> opt3 = Optional.empty();
        opt1.ifPresent(item->System.out.println(item));
        System.out.println(opt1.orElse("world"));
        System.out.println(opt1.orElseGet(()->"world"));
    }
}


public class OptionalTest2 {
    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.setName("xiaoming");
        Employee employee1 = new Employee();
        employee1.setName("xiaohong");


        Company company = new Company();
        company.setName("Sirius");
        List<Employee> employees = Arrays.asList(employee,employee1);
        company.setEmployees(employees);


        Optional<Company> optional = Optional.ofNullable(company);
        optional.map(firm -> firm.getEmployees()).orElse(Collections.emptyList());
    }
}

 

  • 方法引用详解

方法引用实际上是个Lambda表达式的一种语法糖;

我们可以将方法引用看作是一个【函数指针】,function pointer

 

方法引用分为四类:

1、类名::静态方法名

//Lambda表达式形式
students.sort((s1,s2)->Student.compareStudentByScore(s1,s2));
students.forEach(s -> System.out.println(s.getName()));
System.out.println("-----------------------");
//方法引用
students.sort(Student::compareStudentByScore);
students.forEach(s -> System.out.println(s.getScore()));
students.sort(Student::compareStudentByName);
students.forEach(s -> System.out.println(s.getName()));

 

2、引用名(对象名)::实例方法名

//方法引用  引用名::实例方法名
StudentComparator studentComparator = new StudentComparator();
students.sort(studentComparator::compareStudentByScore);
students.forEach(s -> System.out.println(s.getScore()));
students.sort(studentComparator::compareStudentByName);
students.forEach(s -> System.out.println(s.getName()));

 

3、类名::实例方法名

//方法引用  类名::实例方法名
//这是lambda表达式接收的第一个参数来调用
students.sort(Student::compareByScore);
students.forEach(s -> System.out.println(s.getScore()));
students.sort(Student::compareByName);
students.forEach(s -> System.out.println(s.getName()));

 

4、构造方法引用:类名::new

MethodReferenceDemo mrd = new MethodReferenceDemo();
System.out.println(mrd.getString(String::new));
System.out.println("-------------------------");
System.out.println(mrd.getString2("hello",String::new));

 

  • 方法引用场景剖析与默认方法分析

public interface MyInterface1 {
    default void myMythod(){
        System.out.println("MyInterface1");
    }
}
public interface MyInterface2 {
    default void myMethod(){
        System.out.println("MyInterface2");
    }
}
public class MyClass implements MyInterface1,MyInterface2{
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.myMythod();
    }


    @Override
    public void myMythod() {
        MyInterface2.super.myMethod();
    }
}

场景二:

public interface MyInterface1 {
    default void myMythod(){
        System.out.println("MyInterface1");
    }
}
public interface MyInterface2 {
    default void myMethod(){
        System.out.println("MyInterface2");
    }
}
public class MyInterface1Impl implements MyInterface1{
    @Override
    public void myMythod() {
        System.out.println("MyInterface1Impl");
    }
}
/**
 * 实现类的优先级要高于接口,优先使用继承的类中的方法
 */
public class MyClass extends MyInterface1Impl implements MyInterface2{
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.myMythod();
    }
}

Java8中为什么会引用默认方法这个概念?

为了保证能够向后兼容,例如我们在list中要增加某个方法,但是它的实现类不可能一个一个去修改,所以定义了默认方法,那些实现类默认的拥有了这个新增的方法。

 

 

  • Stream介绍与操作方式详解

A sequence of elements supporting sequential and parallel aggregate operations.

链式编程风格。stream pipeline

流由三部分组成:

1、源

2、零个或多个中间操作

3、终止操作

流操作分为两种类型:

1、惰性求值

2、及早求值

public class StreamTest {
    public static void main(String[] args) {
        Stream stream = Stream.of("hello","world","hi");
        String[] myArray = new String[]{"hello","world","hey"};
        Stream stream1 = Stream.of(myArray);
        Stream stream2 = Arrays.stream(myArray);
        List<String> list = Arrays.asList(myArray);
        Stream stream3 = list.stream();
        System.out.println("=================");
        IntStream.of(new int[]{5,6,7}).forEach(System.out::println);
        //包含3不好含8
        IntStream.range(3,8).forEach(System.out::println);
        IntStream.rangeClosed(3,8).forEach(System.out::println);
        System.out.println("=================");
        List<Integer> lists = Arrays.asList(1,2,3,4,5,6,7,8,9);
        lists.stream().map(i -> i*2).reduce(0,Integer::sum);
    }
}

 

  • Stream深度解析与源码实践

 

流(stream)

Collection提供了新的stream()方法;

流不存储值,通过管道的方式获取值;

本质是函数式的,对流的操作会生成一个结果,不过并不会修改该底层的数据源,集合可以作为流的底层数据源;

延迟查找,很多流操作(过滤、映射、排序等)都是可以延迟实现的。

public class StreamTest2 {
    public static void main(String[] args) {
        /**
         * Returns a sequential ordered stream 
         * whose elements are the specified values.
         * of()
         */
        Stream<String> stream = Stream.of("hello","hey","hi");
        //String[] stringArray = 
        //  stream.toArray(length -> new String[length]);
        /**
         * Returns an array containing the elements of this stream.
         *
         */
        String[] stringArray = stream.toArray(String[]::new);
        Arrays.asList(stringArray).forEach(System.out::println);


        //List<String> list = stream.collect(Collectors.toList());
        /**
         * <R> R collect(Supplier<R> supplier,
         * BiConsumer<R, ? super T> accumulator,
         * BiConsumer<R, R> combiner);
         */
        // List<String> list = stream.collect(
        //         ()->new ArrayList<String>(),
        //         (theList,item) -> theList.add(item),
        //         (theList1,theList2)->theList1.addAll(theList2));
        /**
         * @param <R> type of the result
         * @param supplier a function that creates a new result 
         * container. For a parallel execution, this function 
         * may be called multiple times and must return a fresh 
         * value each time.
         * @param accumulator an associative,non-interfering,
         * stateless function for incorporating an additional
         * element into a result
         * @param combiner an associative,non-interfering,
         * stateless function for combining two values, 
         * which must be compatible with the accumulator function
         */
        //stream
        //.collect(LinkedList::new,LinkedList::add,LinkedList::addAll)
        //.forEach(System.out::println);
        stream.collect(Collectors.toCollection(ArrayList::new)).forEach(System.out::println);
        String str = stream.collect(Collectors.joining()).toString();
        System.out.println(str);
    }
}
  • Stream实例剖析

public class StreamTest3 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "hey", "hi");
        /**
        list.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList())
            .forEach(System.out::println);


        Stream<List<Integer>> stream = Stream.of(
                Arrays.asList(1),
                Arrays.asList(2,3),
                Arrays.asList(4,5,6,7));


        stream.flatMap(theList -> theList.stream())
              .map(item -> item*item)
              .forEach(System.out::println);
        */
        Stream<String> stream1 = 
                    Stream.generate(UUID.randomUUID()::toString);
        /**
         * Returns an describing the first element of this stream,
         * or an empty if the stream is empty.  If the stream has
         * no encounter order, then any element may be returned.
         */
        stream1.findFirst().ifPresent(System.out::println);


        Stream.iterate(1,item -> item + 2)
              .limit(6).filter(item -> item > 2)
              .mapToInt(item -> item * 2)
              .skip(2)
              .limit(2)
              .sum();
              
        Stream.iterate(1,item -> item + 2)
              .limit(6).filter(item -> item > 2)
              .mapToInt(item -> item * 2)
              .skip(2)
              .limit(2)
              .min()
              .ifPresent(System.out::println);
              //注意Optional的使用,如果为NULL则返回
              


        IntSummaryStatistics iss = Stream.iterate(1, item -> item + 2)
        .limit(6).filter(item -> item > 2)
        .mapToInt(item -> item * 2)
        .skip(2)
        .limit(2)
        .summaryStatistics();
        iss.getMin();
        iss.getMax();
        iss.getCount();
        iss.getAverage();
}
  • Stream陷阱剖析

/**

* 使用过的stream不能重复使用,

* stream关闭之后不能再被使用。

*/
  • 内部迭代与外部迭代本质剖析及流本源分析

select name 
from student 
where age > 20 and address = 'beijing' order by age desc;
描述性的语言:
students.stream()
        .filter(student -> student.getAge() > 20)
        .filter(student -> "beijing".equals(student.getAddress())
        .sorted(...)
        .foreach(student -> System.out.println(student.getName()));

内部迭代和外部迭代:

集合关注的是数据与数据存储本身;

关注的是对数据的计算;

流与迭代器类似的一点是:流是无法重复使用或消费的。

 

流的中间操作都会返回一个Stream对象,例如:Stream<Integer>,Stream<String>等等。

终止操作则不会返回Streamleixingn,可能不返回值,也可能返回其他类型的单值。

 

  • 流的短路与并发流

public class StreamTest4 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(5000000);
        for (int i=0;i<50000000;i++){
            list.add(UUID.randomUUID().toString());
        }
        System.out.println("start sorting~~~~~");
        long startTime = System.nanoTime();
        list.parallelStream().sorted().count();
        long endTime = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS
                              .toMillis(endTime-startTime);
        System.out.println("sort used "+ millis +"ms");
    }
}


public class StreamTest5 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello world","world welcome","hello world hey","world welcome");
        //长度为5的第一个单词
        list.stream().mapToInt(item->item.length())
            .filter(length->length==5)
            .findFirst()
            .ifPresent(System.out::println);


        list.stream().mapToInt(item->{
            int length = item.length();
            System.out.println(item);
            return length;
        }).filter(length->length==5)
          .findFirst()
          .ifPresent(System.out::println);


        list.stream().map(item->item.split(" "))
                     .flatMap(Arrays::stream)
                     .distinct()
                     .collect(Collectors.toList())
                     .forEach(System.out::println);
    }
}
  • Stream分组与分区详解

flatMap的使用场景举例:

public class StreamTest6 {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("Hi","Hello","你好");
        List<String> list2 = Arrays.asList("zhangsan","lisi","wangwu","zhaoliu");
        list1.stream().flatMap(item->list2.stream()
                      .map(item2->item+" "+item2))
                      .collect(Collectors.toList())
                      .forEach(System.out::println);
    }
}

分组操作:

public class SreamTest7 {
    public static void main(String[] args) {
        Student stu1 = new Student("xiaoming",16,30);
        Student stu2 = new Student("xiaohong",15,90);
        Student stu3 = new Student("xiaogang",15,50);
        Student stu4 = new Student("xiaoming",17,60);
        List<Student> list = Arrays.asList(stu1,stu2,stu3,stu4);


        //对学生按照姓名进行分组
        Map<String,List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getName));
        Map<Integer,List<Student>> map1 = list.stream().collect(Collectors.groupingBy(Student::getAge));
        Map<String,Long> map2 = list.stream().collect(Collectors.groupingBy(Student::getName,Collectors.counting()));
        Map<String,Double> map3 = list.stream().collect(Collectors.groupingBy(Student::getName,Collectors.averagingDouble(Student::getScore)));

        //分区
        Map<Boolean,List<Student>> map4 = list.stream().collect(Collectors.partitioningBy(stu->stu.getScore()>80));
    }
}
  • Collector源码分析与收集器核心

Collec:收集器;

Collector作为collect方法得参数;

Collector是一个接口,它是一个可变的汇聚操作,将输入元素累积到一个可变的结果容器中;它会在所有元素都处理完毕之后,将累积的结果转换为一个最终的表示(这是一个可选操作);它支持串行与并行两种方式执行。

/**
 * 
 * @param <T> the type of input elements to the reduction operation
 * @param <A> the mutable accumulation type of 
 * the reduction operation (often hidden as an implementation detail)
 * @param <R> the result type of the reduction operation
 */
public interface Collector<T, A, R> {

Collectors本身提供了关于Collector的常见的汇聚实现,Collectors本身实际上是一个工厂;

A Collector is specified by four functions that work together to accumulate 
entries into a mutable result container, and optionally perform a final transform 
on the result. They are:

creation of a new result container (supplier()})
incorporating a new data element into a result container (accumulator())
combining two result containers into one (combiner())
performing an optional final transform on the container (finisher())

supplier()是一个不传入参数,返回一个容器,里面有一个get()方法;
accumulator()是一个累加函数,将元素添加到容器中;
combiner()是一个规约函数,将两个容器中的数据合并到一个容器中;
finisher()是一个最终转化函数,将元素从一个类型转换成其他的类型。

提供一组特征集合,说明可以对目标执行一个怎样的操作动作。

enum Characteristics {

CONCURRENT,

UNORDERED,

}

A sequential implementation of a reduction using a collector would create 
a single result container using the supplier function, and invoke the accumulator 
function once for each input element. A parallel implementation would 
partition the input, create a result container for each partition, 
accumulate the contents of each partition into a subresult for that partition,
and then use the combiner function to merge the subresults into a combined result.
大体意思:顺序执行只会使用supplier函数创建一个容器,对每个输入元素调用聚集函数;并行执行会将
输入进行分区,为每一个分区创建一个容器,将每个容器中的元素聚集到一个子结果集中,最后对所有的子集合
合并成一个。


 

  • Collector同一性与结合性分析

为了确保串行与并行操作结果的等价性,Collector函数需要满足两个条件:identity(同一性)与associativity(结合性)

identity:

针对任何部分累计结果和一个空的结果容器合并的时候必须满足

combiner.apply(a, supplier.get()) 即结果 a 和一个空的结果容器合并,得到的结果还是 a

 

The associativity constraint says that splitting the computation must produce an equivalent result.

串行的操作:

A a1 = supplier.get();

accumulator.accept(a1, t1);

accumulator.accept(a1, t2);

R r1 = finisher.apply(a1); // result without splitting

并行的操作:

A a2 = supplier.get();

accumulator.accept(a2, t1);

A a3 = supplier.get();

accumulator.accept(a3, t2);

R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting

对于无需的收集器:比较两个集合等价的条件是两个集合的元素相同不考虑顺序;

对于有序的收集器

finisher.apply(a1).equals(finisher.apply(a2))

 

  • Collector复合与注意事项

The first argument passed to the accumulator function, both arguments passed to the combiner function, and the argument passed to the finisher function must be the result of a previous invocation of the result supplier, accumulator, or combiner functions.

T是流动的元素类型、A是每次操作的中间结果容器的类型、R是最终返回的结果的类型

public interface Collector<T, A, R> {

    Supplier<A> supplier();
    BiConsumer<A, T> accumulator();
    BinaryOperator<A> combiner();
    Function<A, R> finisher();

}

Collector具体的实现过程:

Performing a reduction operation with a Collector should produce a result equivalent to:
{
    R container = collector.supplier().get();
    for (T t : data)
        collector.accumulator().accept(container, t);
    return collector.finisher().apply(container);
}
  • 收集器用法详解与多级分组和分区

Collector的实现类CollectorImpl被作为了静态内部类,其实是它本身就是一个工厂类,提供了比较常见的实现方法。

函数是编程最大的特点:表示做什么,而不是如何做,传递的是动作。

Implementations of Collector that implement various useful reduction operations, such as accumulating elements into collections, summarizing elements according to various criteria, etc.The following are examples of using the predefined collectors to perform common mutable reduction tasks:

// Accumulate names into a List
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());

// Accumulate names into a TreeSet
Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));


// Convert elements to strings and concatenate them, separated by commas
String joined = things.stream().map(Object::toString).collect(Collectors.joining(", "));


// Compute sum of salaries of employee
int total = employees.stream().collect(Collectors.summingInt(Employee::getSalary)));


// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));


// Compute sum of salaries by department
Map<Department, Integer> totalByDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));


// Partition students into passing and failing
Map<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
public class StreamTest {
    public static void main(String[] args) {
        Student stu1 = new Student("zhangsan",100);
        Student stu2 = new Student("lisi",50);
        Student stu3 = new Student("wangwu",80);
        Student stu4 = new Student("xiaoming",85);
        Student stu5 = new Student("xiaoming",90);


        List<Student> list = Arrays.asList(stu1,stu2,stu3,stu4,stu5);
        list.stream().collect(Collectors.toList());


        list.stream().collect(Collectors.counting());
        list.stream().count();
        List<Student> list1 = new ArrayList<>();
        list.stream()
            .collect(minBy(Comparator.comparingInt(Student::getScore)))
            .ifPresent(list1::add);

        list1.forEach(System.out::println);

        list.stream()
            .collect(maxBy(Comparator.comparingInt(Student::getScore)))
            .ifPresent(System.out::println);

        list.stream().collect(averagingInt(Student::getScore));
        list.stream().collect(summarizingInt(Student::getScore));

        IntSummaryStatistics intSummaryStatistics = list.stream()
                .collect(summarizingInt(Student::getScore));
        System.out.println(intSummaryStatistics);


        list.stream().map(Student::getName).collect(joining());
        list.stream().map(Student::getName).collect(joining(","));
        list.stream().map(Student::getName).collect(joining(",","<begin>","<end>"));


        Map<Integer, Map<String,List<Student>>> map = 
       list.stream().collect(groupingBy(Student::getScore,groupingBy(Student::getName)));
        System.out.println(map);


        Map<Boolean,List<Student>> map1 = 
        list.stream().collect(partitioningBy(student -> student.getScore() > 80));
        System.out.println(map1);


        Map<Boolean, Map<Boolean,List<Student>>> map2 = 
        list.stream().collect(partitioningBy(student-student.getScore() > 80,
                              partitioningBy(student->student.getScore()>90))
                      );
        System.out.println(map2);


        Map<String,Student> map3 = 
        list.stream().collect(groupingBy(Student::getName,
    collectingAndThen(minBy(Comparator.comparingInt(Student::getScore)),Optional::get)));
        System.out.println(map3);

 

  • 比较器详解与类型推断特例

public class MyComparator {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("nihao","hello","world","welcome");
        Collections.sort(list,(item1,item2)->item1.length()-item2.length());
        Collections.sort(list,(item1,item2)->item2.length()-item1.length());
        Collections.sort(list,Comparator.comparingInt(String::length).reversed());

        //为什么这里无法推断出item的类型
        Collections.sort(list, Comparator.comparingInt((String item)->item.length()).reversed());
        System.out.println(list);
        /**
         * thenComparing的执行必须是前面的比较器返回为0的
         */
        Collections.sort(list,
            Comparator.comparingInt(String::length).reversed().thenComparing(
                Comparator.comparing(String::toLowerCase,Comparator.reverseOrder())
            )
        );

    }
}
  • 自定义收集器实现

/**
 * 流动元素类型T
 * 中间结果容器Set<T>
 * 返回结果容器Set<T>
 * @param <T>
 */
public class MySetCollector<T> implements Collector<T, Set<T>,Set<T>> {
    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("supplier invoked!");
        return HashSet::new;
    }


    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked!");
        //不能这样用的原因是因为
        //return HashSet<T>::add;
        //return (set,item)->set.add(item);
        return Set<T>::add;
    }


    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked!");
        return (set1,set2)->{
            set1.addAll(set2);
            return set1;
        };
    }


    @Override
    public Function<Set<T>, Set<T>> finisher() {
        System.out.println("finisher invoked!");


        return Function.identity();
    }


    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked!");
        /**
         * IDENTITY_FINISH
         * 表示直接将中间结果的类型强制转换成返回结果的类型
         */
        return Collections.unmodifiableSet(
                EnumSet.of(IDENTITY_FINISH,UNORDERED));
    }


    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello","world","welcome");
        list.stream().collect(new MySetCollector<>()).forEach(System.out::println);
    }
}
  • 自定义收集器深度剖析与并行流陷阱

public class MyMapCollector<T> implements Collector<T, Set<T>, Map<T,T>> {
    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("supplier invoked!");
        return HashSet::new;
    }


    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked!");
        return Set<T>::add;
    }


    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked!");
        return (set1,set2)->{
            set1.addAll(set2);
            return set1;
        };
    }


    @Override
    public Function<Set<T>, Map<T,T>> finisher() {
        System.out.println("finisher invoked!");
        return set -> {
            Map<T,T> map = new HashMap<>();
            set.stream().forEach(item ->  map.put(item,item));
            return map;
        };
    }


    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked!");
        //这里如果加上IDENTITY_FINISH会报错,类型转换错误
        return Collections.unmodifiableSet(EnumSet.of(UNORDERED));
    }


    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello","world","welcome");
        list.stream().collect(new MyMapCollector<>()).forEach((k,v)->{
            System.out.println(k+":"+v);
        });
    }
}
  • 收集器枚举特性深度解析与并行流原理

  • Collectors工厂类源码分析与实战

  • groupingBy源码分析
  • partioningBy与groupingByConcurrent源码分析
  • Stream源码分析
  • Stream与BaseStream源码分析
  • 分割迭代器源码剖析
  • 分割迭代器与ForkJoin详解
  • 分割迭代器实现分析
  • OfPrimitive与OfInt实现原理剖析
  • 流源构造代码分析
  • ReferencePipeline与AbstractPipeline源码深度解读
  • IteratorSpliterator与流源操作方式详解
  • 流调用机制与原理
  • Sink与opWrapSink源码剖析
  • TerminalOp源码分析与终止操作层次体系
  • 流延迟求值底层分析与Sink链接机制揭秘
  • Stream中间操作与终止操作层次体系分析与设计思想剖析
  • Joda项目介绍与实战
  • Java 8全新日期和时间API详解与UTC介绍
  • Java 8全新日期与时间API实战
  • Java 8深入剖析与实战课程总结与展望

墨客地址:https://shimo.im/docs/c6pKXDxCgTXhh6gQ/ 「Java8」

未完。。。。。。待续。。。。。。。(精力不够用,总是感觉很累~以后再补上~ 我是不是买两盒某宝片补一下 O(∩_∩)O)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值