lambda & stream

函数式编程

1. lambda

1.1 函数式编程思想概述

  • 面向对象的思想:
    做一件事,找一个能解决这个事情的对象,调用对象的方法完成事情。
  • 函数式编程的思想:
    只要能获取到结果就可以了,谁去做,怎么做都无所谓。只关心结果不关心过程

1.2 lambda表达式的演化

1. 匿名内部类
    new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("创建了一个新的线程1");
            }
        }).start();
// 我们使用内部类的根部目的是将这个方法传进去,具体一点就是将 用于打印的这条语句传进去,而创建Runnable这个类了是没有必要的。
2. lambda表达式(最优写法)
    new Thread(()-> System.out.println("使用lambda表达式创建了一个新的进程!")).start();

// 注意:可以把lambda表达式看作是匿名内部类的一种演变,但是二者还是有所不同的。匿名内部类会生成一个匿名内部类的文件,但是lambda表达式不会出现这种问题。

1.3 lambda表达式的标准格式

  • 参数、箭头、方法 (参数列表)-> {一些重写方法中的代码}

    ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔。

    ->:传递的意思,把参数传递给方法体

    {}:重写接口的抽象方法的方法体

  • demo

1. 创建一个接口,这个接口中只有一个抽象方法
    public interface Cook {
    	/**
         * 定义一个做饭的抽象方法
         */
    	void makeFood();
	}
2. 创建一个方法,这个方法的参数就是上面这个抽象类
    public static void eatFood(Cook cook){
        cook.makeFood();
    }
3. 调用该方法
    public static void main(String[] args) {
        eatFood(()-> System.out.println("厨师做饭"));
    }
    

1.4 lambda表达式简化规则

  • lambda表达式:可推导,可省略

​ 凡是根据上下文推导出来的内容都可以省略书写

  • lambda表达式可省略的内容:

    1. (参数列表):参数列表中的数据类型,可以省略不写。

    2. (参数列表):括号中的参数如果只有一个,那么类型和()都可以省略

    3. {一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略{},return,‘;’分号

      注意:要省略必须这三个一起省略

1.5 lambda表达式的使用前提

  1. 使用lambda表达式必须具有接口,且要求接口有且仅有一个抽象方法。但是可以有其他的静态或者默认的方法,私有方法也是可以的。

  2. 使用lambda必须具有上下文推断。

    也就是方法的参数或局部变量类型必须为lambda对应的接口类型,才能使用lambda作为该接口的实例。
    备注:有且仅有一个抽象方法的接口,称为“函数式接口”

1.6 @FunctionalInterface

  • 作用:可以检测接口是否是一个函数式接口

    1. 如果是函数时接口编译就会成功

    2. 如果不是函数式接口编译就会失败

      通常造成编译失败的原因有,没有抽象方法,或者抽象方法不止一个

2. 函数式编程

lambda表达式有延时执行的特点,可以利用这个特点对一些代码进行优化

2.1 lambda作为参数和返回值

1. lambda作为参数使用的场景经常出现
    new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("创建了一个新的线程1");
            }
        }).start();
2. lambda作为返回值使用
    public static Cook getCook(){
        return ()-> System.out.println("你获取到了一个厨师");
    }
	// 这种方式也是挺常见的,但是没有作为参数常见。其本质就是生成一个实现了该接口的匿名内部类。但是该过程没有生成匿名内部类的文件。
    

2.2 常用的函数式接口

通常这些接口都被放在java.util.function包中

2.2.1 Supplier接口 —英文翻译 供应商
package java.util.function;

/**
 * Represents a supplier of results. 表示结果的提供者
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.不需要每次调用供应商时都返回新的或不同的结果
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier 由该供应商提供的返回结果的类型
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
  • 作用:用来获取一个泛型参数指定类型的对象数据
  • Supplier<T> 接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据。
// 定义一个方法,该方法将一个函数式接口作为参数,所以在调用该方法的时候可以使用lambda来传递参数。    本质: 在调用该方法的时候 会生成一个该接口的对象,即匿名内部类。lambda表达式中就是该类中唯一抽象函数的实现。 而在该方法中使用了该匿名内部类去掉用了唯一抽象方法的实现。
public static User getUser(Supplier<User> supplier){
        return supplier.get();
    }

User zhanghan = getUser(() -> new User("zhanghan", 18));
System.out.println(zhanghan);

// 而 Supplier<T> 该接口存在的意义,就是不用我们自己再去写一个这种获取对象的函数式接口
2.2.2 Consumer接口
package java.util.function;

import java.util.Objects;

/**
 * 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.
 * 表示接受单个输入参数但不返回结果的操作。与大多数其他功能接口不同,消费者需要通过副作用进行操作。
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
  • Consumer<T>接口正好有Supplier<T>接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
  • void accept(T t);
public static void accept(Consumer<User> userConsumer,User user){
        userConsumer.accept(user);
    }

accept((user)-> System.out.println(user),zhanghan);

// 不要被这个函数式接口的名字所迷惑,虽然消费的确形象却也容易限制思维。 这个函数式接口的作用就是给定一个参数,对这个参数做操作还不用返回值。 需要将,操作方法和对象一起当作参数传进方法。  
  • default Consumer andThen(Consumer<? super T> after)

    作用:需要两个Consumer接口,可以把两个consumer接口组合到一起,再对数据进行消费。

1. 本质:一共生成了三个匿名内部类,而第三个匿名内部类的accept方法就是前两个accept方法的集合。
2. public static void andThenAccept(Consumer<String> c1,Consumer<String> c2,String string){
        c1.andThen(c2).accept(string);   // 谁在前面就先消费谁
    }
	
	andThenAccept((t1)-> System.out.println(t1.toUpperCase())
                ,(t2)-> System.out.println(t2.toLowerCase())
                ,"Hello");
3. 分析:
    a. 首先会根据(t1)-> System.out.println(t1.toUpperCase())创建一个匿名内部类c1,
    它的accept方法 accept(String str){System.out.println(t1.toUpperCase());}
	b. 根据(t2)-> System.out.println(t2.toLowerCase() 创建一个匿名内部类c2,
    它的accept方法 accept(String str){System.out.println(t1.toLowerCase());}
    c. 创建一个String 对象 string = "Hello"
    d. 执行方法体中的代码。
       c1.andThen(c2)    => return (string) -> { c1.accept(string); c2.accept(string); }  => c3 匿名内部类 c3的accetp方法 accept(string){c1.accept(string),c2.accept(string)}
    e. c3.accept(string)
2.2.3 Predicate接口
package java.util.function;

import java.util.Objects;

/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
  • 本质:给它一个对象和一个判定条件他去判断这个对象符不符合这个条件,然后给你一个返回
1. boolean test(T t)
    public static boolean isTure(Predicate<String> predicate,String str){
        return predicate.test(str);
    }
	
	System.out.println(isTure((str)->{if ("zhanghan".equals(str)) return true; else return false;},"zhang"));
2. default Predicate<T> negate()
   public static boolean isNegateTure(Predicate<String> predicate,String str){
        return predicate.negate().test(str);
    }
	
	System.out.println(isNegateTure((str)->{if ("zhanghan".equals(str)) return true; else return false;},"zhang"));
	// 分析:一共会创建两个匿名内部类,第二个匿名内部类调用了第一个匿名内部类的test方法
3. default Predicate<T> and(Predicate<? super T> other)
    public static boolean isAndTure(Predicate<String> predicate,Predicate<String> predicate2,String str){
        return predicate.and(predicate2).test(str);
    }
	
	System.out.println(isAndTure((str1)->"zhanghan".equals(str1)?true:false
                ,(str2)->str2.length() > 3,"zhanghan"));
	// 分析:一共创建了三个匿名内部类,在第三个匿名内部类的test方法中做了一个与的判断。
4. default Predicate<T> or(Predicate<? super T> other)
    public static boolean isOrTure(Predicate<String> predicate,Predicate<String> predicate2,String str){
        return predicate.or(predicate2).test(str);
    }
	
	System.out.println(isOrTure((str1)->"zhan".equals(str1)?true:false
                ,(str2)->str2.length() > 3,"zhanghan"));
5. static <T> Predicate<T> isEqual(Object targetRef)
    // todo   这个后期补上 等学完stream相关的知识
2.2.4 Function接口 --感觉更实用一些
package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}
  • 本质:将一个对象传入进行相关的操作返回,并且返回的结果还不用和传入时相同
1. R apply(T t)
    public static String convert(Function<Integer,String> f1,Integer n1){
        return f1.apply(n1);
    }

	System.out.println(convert(x -> String.valueOf(x) + "nb",23));
2. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
    public static Object convert2(Function<Integer,String> f1,Function<String,List> f2,Integer n1){
        return f1.andThen(f2).apply(n1);
    }
	
	System.out.println(convert2(x -> String.valueOf(x) + "nb",y -> {List<String> list = new ArrayList<>();
            list.add(y);list.add("zhanghan");
        return list;},23));
	// 分析:主要过程就是创建三个匿名内部类,在第三个匿名内部类中,第一个转换的结果作为第二个转换的原数据。 要注意的是继承关系,
3. default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
    // 作用和上一个一样也是为了连续的类型变换
4. static <T> Function<T, T> identity()
    Function.identity()  // 只能使用这种方式调用

3. stream流

  • 这里所说的stream流不是I/O stream流 I/O流主要是进行读写操作,这里的stream流主要是对集合的一些操作。

3.1 流式思想概述

  1. 这里的流的概念更类似于流水线的概念
  2. 得益于lambda表达式的延迟执行的特性。在流的过成功集合元素并不会真的被处理,只有到最后的时候才会按照指定策略执行操作。
  3. 流的执行过程并不会存储数据,只是对数据进行运算,就像是生产的流水线一样,每一个步骤只会对商品进行加工,而不会将商品留下来。
  4. 数据源 流的来源,可以是集合,数组等。
  5. Pipelining:中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格。这样做可以对操作进行优化,比如延迟执行和短路。
  6. 内部迭代:以前对集合遍历都是通过Iterator或者增强for的方式。显式的在集合外部进行迭代,这叫做外部迭代。stream提供了内部迭代的方式,流可以直接调用遍历方法。
  7. 使用步骤:a. 获取一个数据源 ->b. 数据转换 ->c. 执行操作获取想要的结果, 每次转换原有的stream对象不改变,返回一个新的stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。

3.2 获取流

java.util.stream.Stream<T>是Java 8 新加入的最常用的流接口。(这并不是一个函数式接口)

  • 所有的Collection集合都可以通过stream默认方式获取流。
  • Stream接口的静态方法of可以获取数组对应的流。

3.3 常用方法

  • 延迟方法:返回值类型仍然是stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余的方法均为延迟方法。)
  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持链式调用。
  • 注意:延迟操作所返回的stream对象已经不是原先的
3.3.1 逐一处理:forEach
/**
     * Performs an action for each element of this stream.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal
     * operation</a>.
     *
     * <p>The behavior of this operation is explicitly nondeterministic.
     * For parallel stream pipelines, this operation does <em>not</em>
     * guarantee to respect the encounter order of the stream, as doing so
     * would sacrifice the benefit of parallelism.  For any given element, the
     * action may be performed at whatever time and in whatever thread the
     * library chooses.  If the action accesses shared state, it is
     * responsible for providing the required synchronization.
     *
     * @param action a <a href="package-summary.html#NonInterference">
     *               non-interfering</a> action to perform on the elements
     */
    void forEach(Consumer<? super T> action);


// 使用
List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");
        list.add("g");
        list.stream().forEach(x -> System.out.println("字符串是:"+ x));
3.3.2 过滤:filter
/**
     * Returns a stream consisting of the elements of this stream that match
     * the given predicate.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                  <a href="package-summary.html#Statelessness">stateless</a>
     *                  predicate to apply to each element to determine if it
     *                  should be included
     * @return the new stream
     */
    Stream<T> filter(Predicate<? super T> predicate);


// 使用
list.stream().filter(x -> x.charAt(0) >'c' && x.charAt(0) <'f'?true:false).forEach(x -> System.out.println(x));

// ps:String 与 String之间是不能直接进行比较操作的。但是char之间是可以的
3.3.3 映射:map
 /**
     * Returns a stream consisting of the results of applying the given
     * function to the elements of this stream.
     *
     * <p>This is an <a href="package-summary.html#StreamOps">intermediate
     * operation</a>.
     *
     * @param <R> The element type of the new stream
     * @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
     *               <a href="package-summary.html#Statelessness">stateless</a>
     *               function to apply to each element
     * @return the new stream
     */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
// 使用
Stream.of("123456").map(x -> Integer.parseInt(x)).forEach(x-> System.out.println(x.getClass()));
3.3.4 统计个数:count
/**
     * Returns the count of elements in this stream.  This is a special case of
     * a <a href="package-summary.html#Reduction">reduction</a> and is
     * equivalent to:
     * <pre>{@code
     *     return mapToLong(e -> 1L).sum();
     * }</pre>
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
     *
     * @return the count of elements in this stream
     */
    long count();

// 使用
System.out.println(list.stream().count());
3.3.5 截取前几个:limit
/**
     * Returns a stream consisting of the elements of this stream, truncated
     * to be no longer than {@code maxSize} in length.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting
     * stateful intermediate operation</a>.
     *
     * @apiNote
     * While {@code limit()} is generally a cheap operation on sequential
     * stream pipelines, it can be quite expensive on ordered parallel pipelines,
     * especially for large values of {@code maxSize}, since {@code limit(n)}
     * is constrained to return not just any <em>n</em> elements, but the
     * <em>first n</em> elements in the encounter order.  Using an unordered
     * stream source (such as {@link #generate(Supplier)}) or removing the
     * ordering constraint with {@link #unordered()} may result in significant
     * speedups of {@code limit()} in parallel pipelines, if the semantics of
     * your situation permit.  If consistency with encounter order is required,
     * and you are experiencing poor performance or memory utilization with
     * {@code limit()} in parallel pipelines, switching to sequential execution
     * with {@link #sequential()} may improve performance.
     *
     * @param maxSize the number of elements the stream should be limited to
     * @return the new stream
     * @throws IllegalArgumentException if {@code maxSize} is negative
     */
    Stream<T> limit(long maxSize);

// 使用
list.stream().limit(4).forEach(s -> System.out.println(s));
3.3.6 跳过前几个:skip
 /**
     * Returns a stream consisting of the remaining elements of this stream
     * after discarding the first {@code n} elements of the stream.
     * If this stream contains fewer than {@code n} elements then an
     * empty stream will be returned.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">stateful
     * intermediate operation</a>.
     *
     * @apiNote
     * While {@code skip()} is generally a cheap operation on sequential
     * stream pipelines, it can be quite expensive on ordered parallel pipelines,
     * especially for large values of {@code n}, since {@code skip(n)}
     * is constrained to skip not just any <em>n</em> elements, but the
     * <em>first n</em> elements in the encounter order.  Using an unordered
     * stream source (such as {@link #generate(Supplier)}) or removing the
     * ordering constraint with {@link #unordered()} may result in significant
     * speedups of {@code skip()} in parallel pipelines, if the semantics of
     * your situation permit.  If consistency with encounter order is required,
     * and you are experiencing poor performance or memory utilization with
     * {@code skip()} in parallel pipelines, switching to sequential execution
     * with {@link #sequential()} may improve performance.
     *
     * @param n the number of leading elements to skip
     * @return the new stream
     * @throws IllegalArgumentException if {@code n} is negative
     */
    Stream<T> skip(long n);

// 使用
list.stream().skip(4).forEach(s -> System.out.println(s));
3.3.7 组合:concat
/**
     * Creates a lazily concatenated stream whose elements are all the
     * elements of the first stream followed by all the elements of the
     * second stream.  The resulting stream is ordered if both
     * of the input streams are ordered, and parallel if either of the input
     * streams is parallel.  When the resulting stream is closed, the close
     * handlers for both input streams are invoked.
     *
     * @implNote
     * Use caution when constructing streams from repeated concatenation.
     * Accessing an element of a deeply concatenated stream can result in deep
     * call chains, or even {@code StackOverflowException}.
     *
     * @param <T> The type of stream elements
     * @param a the first stream
     * @param b the second stream
     * @return the concatenation of the two input streams
     */
    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
        Objects.requireNonNull(a);
        Objects.requireNonNull(b);

        @SuppressWarnings("unchecked")
        Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
        Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
        return stream.onClose(Streams.composedClose(a, b));
    }

// 使用
Stream.concat(stream,stream1).forEach(x -> System.out.println(x));
3.3.8 收集:collect
/**
     * Performs a <a href="package-summary.html#MutableReduction">mutable
     * reduction</a> operation on the elements of this stream using a
     * {@code Collector}.  A {@code Collector}
     * encapsulates the functions used as arguments to
     * {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of
     * collection strategies and composition of collect operations such as
     * multiple-level grouping or partitioning.
     *
     * <p>If the stream is parallel, and the {@code Collector}
     * is {@link Collector.Characteristics#CONCURRENT concurrent}, and
     * either the stream is unordered or the collector is
     * {@link Collector.Characteristics#UNORDERED unordered},
     * then a concurrent reduction will be performed (see {@link Collector} for
     * details on concurrent reduction.)
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal
     * operation</a>.
     *
     * <p>When executed in parallel, multiple intermediate results may be
     * instantiated, populated, and merged so as to maintain isolation of
     * mutable data structures.  Therefore, even when executed in parallel
     * with non-thread-safe data structures (such as {@code ArrayList}), no
     * additional synchronization is needed for a parallel reduction.
     *
     * @apiNote
     * The following will accumulate strings into an ArrayList:
     * <pre>{@code
     *     List<String> asList = stringStream.collect(Collectors.toList());
     * }</pre>
     *
     * <p>The following will classify {@code Person} objects by city:
     * <pre>{@code
     *     Map<String, List<Person>> peopleByCity
     *         = personStream.collect(Collectors.groupingBy(Person::getCity));
     * }</pre>
     *
     * <p>The following will classify {@code Person} objects by state and city,
     * cascading two {@code Collector}s together:
     * <pre>{@code
     *     Map<String, Map<String, List<Person>>> peopleByStateAndCity
     *         = personStream.collect(Collectors.groupingBy(Person::getState,
     *                                                      Collectors.groupingBy(Person::getCity)));
     * }</pre>
     *
     * @param <R> the type of the result
     * @param <A> the intermediate accumulation type of the {@code Collector}
     * @param collector the {@code Collector} describing the reduction
     * @return the result of the reduction
     * @see #collect(Supplier, BiConsumer, BiConsumer)
     * @see Collectors
     */
    <R, A> R collect(Collector<? super T, A, R> collector);

// 传入一个收集器
Set<String> set = list.stream().collect(Collectors.toSet());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值