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)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章