JAVA8新特性-Stream

文章目录

      • Stream 流式数据操作
      • 中间操作
        • filter
        • map
        • peek
        • flatMap
      • 最终操作(结束操作)
        • max
        • reduce
        • collect
          • toList
            • 生成指定的List类型
          • toMap
            • Key冲突
            • 生成指定的Map类型
            • toMap 方法浅析
          • groupingBy
          • joining
          • mapping
          • partitioningBy

Stream 流式数据操作

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。让我们写出高效率、干净、简洁的代码。

JAVA8 中 Collection 接口增加了 stream 默认方法。 所以实现了 java.util.Collection 接口的类可以进行流操作

    /**
     * Returns a sequential {@code Stream} with this collection as its source.
     *
     * 

This method should be overridden when the {@link #spliterator()} * method cannot return a spliterator that is {@code IMMUTABLE}, * {@code CONCURRENT}, or late-binding. (See {@link #spliterator()} * for details.) * * @implSpec * The default implementation creates a sequential {@code Stream} from the * collection's {@code Spliterator}. * * @return a sequential {@code Stream} over the elements in this collection * @since 1.8 */ default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }

所以Stream 针对的集合, 操作的也是集合元素。配合lambda表达式可以以简洁代码完成对集合的高效的聚合运算

JAVA8新特性-Stream_第1张图片

  • 中间操作
    • 有状态:只有拿到所有的元素才能处理
    • 无状态: 元素的处理不受之前元素的影响
  • 最终操作
    • 短路。 当集合某个元素满足指定的表达式时,剩余的元素不在执行调用。
    • 非短路。处理集合中所有的元素

stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

中间操作

调用完后,返回一个流(Stream、IntStream、LongStream 、DoubleStream )

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

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

    IntStream mapToInt(ToIntFunction<? super T> mapper);

    LongStream mapToLong(ToLongFunction<? super T> mapper);

    DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

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

    IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
   
    LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
   
    DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);

    Stream<T> distinct();

    Stream<T> sorted();

    Stream<T> sorted(Comparator<? super T> comparator);

    Stream<T> peek(Consumer<? super T> action);

    Stream<T> limit(long maxSize);

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

入参类型是函数式接口 Predicate, 抽象方法 boolean test(T t), 有一个入参,返回类型是Boolean。

返回结果为true的元素才会执行后续操作, 结果为false的元素会被过滤掉。


例如;过滤出,数值大于 3的集合元素

        Integer[] integers = new Integer[]{1, 2, 3, 4, 5};
        List<Integer> result = Stream.of(integers).filter(num -> num > 3).collect(Collectors.toList());
        System.out.println(result);

输出结果:

[4, 5]

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

参数类型是函数式接口Function,抽象方法R apply(T t), 接受一个参数,返回结果。入参类型和返回值类型可以不同。


例如;将字符串类型转化为 整数类型。 下面用到了lambda方法引用

      String[] strings = new String[]{"1", "2", "3"};
      List<Integer> result = Stream.of(strings).map(Integer::valueOf).collect(Collectors.toList());
      //Stream.of(strings).map(t->Integer.valueOf(t)).collect(Collectors.toList());
      System.out.println(result);

当然我们也可以使用IntSteam。 以下代码等价:

       result = Stream.of(strings).mapToInt(Integer::valueOf).collect(ArrayList::new, List::add, (left, right) -> left.addAll(right));

实际上,第三个参数并未调用。

peek
    Stream<T> peek(Consumer<? super T> action);

入参类型函数式接口Consumer, 抽象方法 void accept(T t)只有一个入参,没有返回值(void)


例如;将年龄加10岁

        List<Person> lists = new ArrayList<>();
        Person human1 = new Person("human1", 12);
        Person human2 = new Person("human2", 22);
        lists.add(human1);
        lists.add(human2);
        lists = lists.stream().peek(person -> person.setAge(person.getAge()+10)).collect(Collectors.toList());
        System.out.println(lists);

输出结果:

[Person(name=human1, age=22), Person(name=human2, age=32)]

正是因为没有返回值,所有通常peek方法用于处理POJO对象。 当我们试图给集合中的每个元素追加“*”,以下代码功能未生效

        String[] strings = new String[]{"1", "2", "3"};
        List<String> stringList = Stream.of(strings).peek(str -> str = str.concat("*")).collect(Collectors.toList());
        System.out.println(stringList);

[1, 2, 3]

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

Function接口上面提到,抽象方法 R apply(T t)。 从参数的泛型定义可以看出 ,返回类型应当是一个Stream。 首先对集合中的每个元素执行mapper操作,mapper操作后返回一个新的Stream,并用所有mapper返回的Stream中的元素组成一个新的Stream作为最终返回结果。


例如; 字符串拆分

        String str = "1,2;4,5,6";
        List<String> stringList = Stream.of(str.split(";")).flatMap(v-> Stream.of(v.split(","))).collect(Collectors.toList());
        System.out.println(stringList);

输出结果:

[1, 2, 4, 5, 6]

最终操作(结束操作)

结束操作调用完成后,终止调用,返回最终结果。

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

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

    Object[] toArray();

    <A> A[] toArray(IntFunction<A[]> generator);
    
    T reduce(T identity, BinaryOperator<T> accumulator);

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

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

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

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

    Optional<T> min(Comparator<? super T> comparator);

    Optional<T> max(Comparator<? super T> comparator);

    long count();

    boolean anyMatch(Predicate<? super T> predicate);

    boolean allMatch(Predicate<? super T> predicate);

    boolean noneMatch(Predicate<? super T> predicate);

    Optional<T> findFirst();

    Optional<T> findAny();
max

max和min 基本一样,都需要传入一个比较器

 Optional<T> max(Comparator<? super T> comparator);
 Optional<T> min(Comparator<? super T> comparator);

Comparator 是一个函数式接口,两个入参

@FunctionalInterface
public interface Comparator<T>{
    int compare(T o1, T o2);
}


例如;

        List<Integer> numbers = new ArrayList<>();
        for (Integer integer = 0; integer < 10; integer++) {
            numbers.add(integer);
        }

        Optional<Integer> maxValue = numbers.stream().max((o1, o2) -> o1.compareTo(o2));
        Optional<Integer> minValue = numbers.stream().min((o1, o2) -> o1.compareTo(o2));
        // 方法引用方式
        //Optional maxValue = numbers.stream().max(Integer::compareTo);
        //Optional minValue = numbers.stream().min(Integer::compareTo);
        long count = numbers.stream().count();

        System.out.println("maxValue\t:" + maxValue.get());
        System.out.println("minValue\t:" + minValue.get());
        System.out.println("count\t:" + count);

maxValue :9
minValue :0
count :10

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

BinaryOperator 接口继承了函数型接口 BinaryOperator,接收两个参数

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}


例如;

        List<Integer> numbers = new ArrayList<>();
        for (Integer integer = 0;integer<10;integer++){
            numbers.add(integer);
        }
        // 求和
        Optional<Integer> sum = numbers.stream().reduce(Integer::sum);
        //求最大值
        Optional<Integer> max = numbers.stream().reduce(Integer::max);
        //求最小值
        Optional<Integer> min = numbers.stream().reduce(Integer::min);

        System.out.println(sum.get());
        System.out.println(max.orElse(1));
        System.out.println(min.orElse(1));

45
9
0

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

Collectors 工具类, 提供了各种方法,返回Collector
JAVA8新特性-Stream_第2张图片

Collector是一个接口,上述这些方法,返回都是通过实现类 CollectorImpl 实现的

        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Function<A,R> finisher,
                      Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }
        
        // castingIdentity
        CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }
  • supplier: 创建一个接收元素的容器
  • accumulatator: 往新容器添加元素
  • finisher: 最终操作,将结果转化为最终的结果类型
  • combiner: 合并两个结果容器。通常用于 parallelXX 操作
  • characteristics: 定义收集器行为。 并发、无序、 结果无需转化。 这些行为可以组合使用
enum Characteristics {
    /**
     * Indicates that this collector is concurrent, meaning that
     * the result container can support the accumulator function being
     * called concurrently with the same result container from multiple
     * threads.
     *
     * 

If a {@code CONCURRENT} collector is not also {@code UNORDERED}, * then it should only be evaluated concurrently if applied to an * unordered data source. */ CONCURRENT, /** * Indicates that the collection operation does not commit to preserving * the encounter order of input elements. (This might be true if the * result container has no intrinsic order, such as a {@link Set}.) */ UNORDERED, /** * Indicates that the finisher function is the identity function and * can be elided. If set, it must be the case that an unchecked cast * from A to R will succeed. * */ IDENTITY_FINISH }

  static final Set<Collector.Characteristics> CH_CONCURRENT_ID
           = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
                                                    Collector.Characteristics.UNORDERED,
                                                    Collector.Characteristics.IDENTITY_FINISH));
   
   static final Set<Collector.Characteristics> CH_CONCURRENT_NOID
           = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT,
                                                    Collector.Characteristics.UNORDERED));
   
   static final Set<Collector.Characteristics> CH_ID
           = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
           
   static final Set<Collector.Characteristics> CH_UNORDERED_ID
           = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
                                                    Collector.Characteristics.IDENTITY_FINISH));
   static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
toList
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }

扫一眼,我们大概知道。 toList,返回的是一个新的ArrayList, 使用 list.add方法, 将原集合的元素添加到新的集合中。


例如; 将字符串拆分并返回一个List

List<String> filtered = Stream.of("1,2,3".split(",")).collect(Collectors.toList());
生成指定的List类型

Collectors 类中toList 方法没有重载。但是可以通过Collectors.toCollection方法实现。

    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
        return new CollectorImpl<>(collectionFactory, Collection<T>::add,
                                   (r1, r2) -> { r1.addAll(r2); return r1; },
                                   CH_ID);
    }
        List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
        List list = strings.stream().collect(Collectors.toCollection(LinkedList::new));
        System.out.println(list);
        System.out.println(list.getClass());

输出结果;

[abc, , bc, efg, abcd, , jkl]
class java.util.LinkedList

toMap
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper) {
        return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
    }

两个Function参数,R apply(T t)。 用于Map,key-value 的映射关系计算。 看到 HashMap::new就知道一定是创建一个新Map, 然后 利用这两个参数,获得key-value的值。

第三个参数,则告诉我们,如果key冲突,则抛出异常。List to Map, 应当以唯一键作为key

    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

例如;以下常用的List 转Map

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private int age;
}
        List<Person> lists=new ArrayList<>();
        Person human1=new Person("human1",12);
        Person human2=new Person("human2",22);
        lists.add(human1);
        lists.add(human2);
        Map<String, Person> maps = lists.stream().collect(Collectors.toMap(Person::getName,Function.identity()));
        System.out.println(maps);

{human2=Person(name=human2, age=22), human1=Person(name=human1, age=12)}

如果key冲突,

        List<Person> lists=new ArrayList<>();
        Person human1=new Person("human1",12);
        Person human2=new Person("human1",22);
        lists.add(human1);
        lists.add(human2);
        Map<String, Person> maps = lists.stream().collect(Collectors.toMap(Person::getName,Function.identity()));
        System.out.println(maps);

java.lang.IllegalStateException: Duplicate key human1

Function.identity() 方法总是返回输入的参数。即将输入的集合元素原样返回

    static <T> Function<T, T> identity() {
        return t -> t;
    }

这个t->t, 实际是Function的lambda表达式的写法。

Function<Person, Person> function1 = new Function<Person, Person>(){
    @Override
    public Person apply(Person person) {
        return person;
    }
};
// lambda 表达式
Function<Person, Person> function1 = person -> person;
Key冲突

如果不能保证key唯一怎么办呢,上面的方法已经定死。如果key重复,则会抛出异常 java.lang.IllegalStateException: Duplicate key xxxxx

可以用它的重载方法。

    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
        return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
    }

Collectors.toMap 方法第三个参数,可以用于控制key相同时如何处理。

所以通过以下代码,就可以控制key相同的value的取舍。 下面代码的意思是,在Map中,计算的key(name字段)相同时,value的取值以最后一个为准。

Map<String,Object> maps=lists.stream().
        collect(Collectors.toMap(Person::getName, Person::getAge,(val1,val2)->val2));
生成指定的Map类型

默认返回的Map类型为HashMap,当然我们可以在生成Map的时候指定我们需要的类型。

Map<String,Object> maps=lists.stream().collect(Collectors.toMap(Person::getName, Person::getAge,(key1,key2)->key2),TreeMap::new);
System.out.println(maps instanceof  TreeMap);

输出结果

true

toMap 方法浅析

toMap的方法重载,最终会调用下面的方法。

    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                Function<? super T, ? extends U> valueMapper,
                                BinaryOperator<U> mergeFunction,
                                Supplier<M> mapSupplier) {
        BiConsumer<M, T> accumulator
                = (map, element) -> map.merge(keyMapper.apply(element),
                                              valueMapper.apply(element), mergeFunction);
        return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
    }

其中最关键的代码如下, 定义了Map , key-value 的映射取值。还有key冲突时的处理方式。

   BiConsumer<M, T> accumulator
           = (map, element) -> map.merge(keyMapper.apply(element),
                                         valueMapper.apply(element), mergeFunction);

这个调用了Map接口的默认方法merge。 当然了HashMap 重写了merge方法,处理逻辑大概类似。

    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }

If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null. This method may be of use when combining multiple mapped values for a key. For example, to either create or append a String msg to a value mapping:

注意:

  1. 如果key,未关联值或者关联 null值,则使用传入的非 null值(null值会移除键值对)。 否则;
  2. 使用remappingFunction 函数结算结果 替换key关联的值。
  3. 如果remappingFunction 计算的结果为null,那么移除 键值对。
groupingBy
    Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) {
        return groupingBy(classifier, toList());
    }

    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream) {
        return groupingBy(classifier, HashMap::new, downstream);
    }

    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream)


例如;根据实体类name分组

        List<Person> lists = new ArrayList<>();
        Person human1 = new Person("human1", 12);
        Person human2 = new Person("human1", 22);
        lists.add(human1);
        lists.add(human2);
        Map<String, List<Person>> maps = lists.stream().collect(Collectors.groupingBy(Person::getName));
        System.out.println(maps);
        System.out.println(maps.getClass());

输出结果:

{human1=[Person(name=human1, age=12), Person(name=human1, age=22)]}
class java.util.HashMap

从上面构造函数也可以看到, Map 类型是HashMap, List是ArrayList。 我们可以根据需要更改为其他类型。

比如; Map类型是LinkedHashMap, value对应类型LinkedList

maps = lists.stream().collect(Collectors.groupingBy(Person::getName, LinkedHashMap::new , Collectors.toCollection(LinkedList::new)));
joining
joining()
joining(CharSequence delimiter)
joining(CharSequence delimiter,  CharSequence prefix, CharSequence suffix)


例如;

     String[] strArray = "1,2,3,4".split(",");
     String str = Stream.of(strArray).collect(Collectors.joining());
     System.out.println("Collectors.joining(): " + str);
     // 连接集合中的每个元素拼成字符串, 并以“-”为分隔符
     str = Stream.of(strArray).collect(Collectors.joining("-"));
     System.out.println("Collectors.joining(-): "+ str);
     // 连接集合中的每个元素拼成字符串, 并以“-”为分隔符,元素拼接完成在前后追加 前缀和后缀
     str = Stream.of(strArray).collect(Collectors.joining("-", "(", ")"));
     System.out.println("Collectors.joining(-, (,) ): "+str);

输出结果:

Collectors.joining(): 1234
Collectors.joining(-): 1-2-3-4
Collectors.joining(-, (,) ): (1-2-3-4)

mapping

mapping、reduce、maxBy、minBy 实际上完全可以使用Stream中提供的方法。

    Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
                               Collector<? super U, A, R> downstream) 
        String[] strings = new String[]{"1", "2", "3"};
        Set<Integer> set = Stream.of(strings).collect(Collectors.mapping(Integer::valueOf, Collectors.toSet()));
        System.out.println(set);

使用Stream提供的方法,可读性更好一些。

        set = Stream.of(strings).map(Integer::valueOf).collect(Collectors.toSet());
partitioningBy
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
        return partitioningBy(predicate, toList());
    }
        Integer[] integers = new Integer[]{1, 2, 3, 4, 5};
        Map<Boolean, List<Integer>> result = Stream.of(integers).collect(Collectors.partitioningBy((num) -> num > 2));
        System.out.println(result);

输出结果:

{false=[1, 2], true=[3, 4, 5]}

从上面的结果可以看出,partitioningBy是特殊的groupingBy。Predicate运算的结果(true/false)作为Map的key

    result = Stream.of(integers).collect(Collectors.groupingBy((num) -> num > 2));

你可能感兴趣的:(java,java,java8,stream)