Java中Stream配合Lambda使用详解

我们的第一篇文章,主要是通过一个Demo,让大家体验了一下使用流API的那种酣畅淋漓的感觉。如果你没有实践,我还是再次呼吁你动手敲一敲,自己实实在跑一遍上一篇的Demo。相信你的感受和理解也会随之加深的。继续探索流API的高级功能之前,我们先从接口级别全面了解一下流API,这个对于我们来说是至关重要的。接下来,我给大家准备了一张流API关键知识点的UML图。但是大家只需要花一两分钟,整理看一下就可以了,不需要记住,先有个印象,后面我给大家讲解一些关键的方法:

我先整体介绍一下:流API定义的几个接口,都是在java.util.stream包中的.其中上图中的BaseStream接口是最基础的接口,它提供了所有流都可以使用的基本功能:

public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
//....先忽略这些具体的细节
}

由这个接口的定义我们得知,BaseStream是一个泛型接口,它有两个类型参数T和S, 其中T指定了流中的元素的类型,并且由<S extends BaseStream<T, S>>可以知道S必须为BaseStream或BaseStream子类(换句话说,就是S必须是扩展自BaseStream的)。BaseStream继承了AutoCloseable接口。这个接口主要是简化了关闭资源的操作。但是像平时我们操作的集合或数组,基本上都不会出现关闭流的情况。


//由BaseStream接口派生出的流接口包括IntStream ,LongStream,DoubleStream ,Stream<T>
public interface IntStream extends BaseStream<Integer, IntStream> {
}
public interface LongStream extends BaseStream<Long, LongStream> {
}
public interface DoubleStream extends BaseStream<Double, DoubleStream> {
}
 
//这是最具代表性的接口
public interface Stream<T> extends BaseStream<T, Stream<T>> {
//....先忽略这些具体的细节
}

由于Stream接口是最具代表性的,所以我们就选择它来讲解,其实在我们学完Stream接口,其它的三个接口,在使用上基本是一致的了,我们上一篇的Demo基本上也是使用Stream接口来做的练习。我们回想一下:在上一个Demo中我们通过集合框架的stream()方法,就能返回一个流了,它的返回类型就是Stream,比如我们Stream,由此得知,Stream接口里的类型参数T就是流中的元素的类型。木有错,就是这样滴。到这里,整个系列你们已经全部学会了,下课。

BaseStream详解:

public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
     Iterator<T> iterator();//line2
     Spliterator<T> spliterator();//line3
     boolean isParallel();//line4
     S sequential();//line5
     S parallel();//line6
     S unordered();//line7
     S onClose(Runnable closeHandler);//line8
     @Override
     void close();//line10
}
  • Iterator iterator(); //line2
    获得流的迭代器,并返回对该迭代器的引用(终端操作)
  • Spliterator spliterator(); //line3
    获取流的spliterator,并返回其引用(终端操作)
  • boolean isParallel(); //line4
    如果调用流是一个并行流,则返回true;如果调用流是一个顺序流,则返回false。
  • S sequential(); //line5
    基于调用流,返回一个顺序流。如果调用流已经是顺序流了,就返回该流。(中间操作)
  • S parallel(); //line6
    基于调用流,返回一个并行流。如果调用流已经是并行流了,就返回该流。(中间操作)
  • S unordered(); //line7
    基于调用流,返回一个无序流。如果调用流已经是无序流了,就返回该流。(中间操作)
  • S onClose(Runnable closeHandler); //line8
    返回一个新流,closeHandler指定了该流的关闭处理程序,当关闭该流时,将调用这个处理程序。(中间操作)
  • void close(); //line10
    从AutoCloseable继承来的,调用注册关闭处理程序,关闭调用流(很少会被使用到)

终端操作”&”中间操作”

细心的同学应该注意到了,BaseStream接口里面的很多方法都在最后标识了(终端操作)和(中间操作),它们之间的区别是非常重要的。

  • 终端操作 会消费流,这种操作会产生一个结果的,比如上面的 iterator()和 spliterator(),以及上一篇中提到的min()和max(),或者是执行某一种操作,比如上一篇的forEach(),如果一个流被消费过了,那它就不能被重用的。

  • 中间操作 中间操作会产生另一个流。因此中间操作可以用来创建执行一系列动作的管道。一个特别需要注意的点是:中间操作不是立即发生的。相反,当在中间操作创建的新流上执行完终端操作后,中间操作指定的操作才会发生。所以中间操作是延迟发生的,中间操作的延迟行为主要是让流API能够更加高效地执行。

“中间操作”的状态

流的中间操作,可以为分无状态操作有状态操作两种,在无状态操作中,在处理流中的元素时,会对当前的元素进行单独处理。比如:谓词过滤操作,因为每个元素都是被单独进行处理的,所有它和流中的其它元素无关,因此被称为无状态操作;而在有状态操作中,某个元素的处理可能依赖于其他元素。比如查找最小值,最大值,和排序,因为他们都依赖于其他的元素。因此为称为有状态操作。当需要进行并行处理流时,有状态的操作和无状态的区别是非常重要的,因为有状态操作可能需要几次处理才能完成,后面的文章我将会给大家详细地讲,现在只需要正常学习下去就可以了

另外,指出一点,如果大家了解泛型的话,应该知道,泛型的类型参数只能是引用类型,因此Stream操作的对象只能是引用类型的,不能用于基本类型。当然官方早已考虑到这一点了,前面你们看到的IntStream,LongStream,DoubleStream就是官方给我们提供的处理基本类型的流了。此处是不是应该给他们掌声

Stream详解

在有了前面的那些知识作铺垫之后,学Stream接口应该会顺风顺水了。还是先看看Stream的详情先:

ublic interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> filter(Predicate<? super T> predicate);//line2
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);//line3
    IntStream mapToInt(ToIntFunction<? super T> mapper);//line4
    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();//line12
    Stream<T> sorted(Comparator<? super T> comparator);//line13
    Stream<T> peek(Consumer<? super T> action);
    Stream<T> limit(long maxSize);
    Stream<T> skip(long n);
    void forEach(Consumer<? super T> action);//line17
    void forEachOrdered(Consumer<? super T> action);//line18
    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);//line30
    Optional<T> max(Comparator<? super T> comparator);//line31
    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();
 
    // Static factories
 
    public static<T> Builder<T> builder() {
        return new Streams.StreamBuilderImpl<>();
    }
 
 
    public static<T> Stream<T> empty() {
        return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
    }
 
 
    public static<T> Stream<T> of(T t) {
        return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
    }
 
 
    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
        return Arrays.stream(values);
    }
 
 
    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {
            @SuppressWarnings("unchecked")
            T t = (T) Streams.NONE;
 
            @Override
            public boolean hasNext() {
                return true;
            }
 
            @Override
            public T next() {
                return t = (t == Streams.NONE) ? seed : f.apply(t);
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iterator,
                Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
    }
 
 
    public static<T> Stream<T> generate(Supplier<T> s) {
        Objects.requireNonNull(s);
        return StreamSupport.stream(
                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
    }
 
 
    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 filter(Predicate<? super T> predicate); //line2
    产生一个新流,其中包含调用流中满足predicate指定的谓词元素(中间操作)
  • Stream map(Function<? super T, ? extends R> mapper); //line3
    产生一个新流,对调用流中的元素应用mapper,新流中包含这些元素。(中间操作)
  • IntStream mapToInt(ToIntFunction<? super T> mapper); //line4
    对调用流中元素应用mapper,产生包含这些元素的一个新IntStream流。(中间操作)
  • Stream sorted(); //line12
  • Stream sorted(Comparator<? super T> comparator); //line13`
    产生一个自然顺序排序或者指定排序条件的新流(中间操作)
  • void forEach(Consumer<? super T> action); //line17
  • void forEachOrdered(Consumer<? super T> action); //line18
    遍历了流中的元素(终端操作)
  • Optional min(Comparator<? super T> comparator) //line30
  • Optional max(Comparator<? super T> comparator); //line31
    获得流中最大最小值,比较器可以由自己定义,也可以使用JDK提供的(终端操作)

一、概述
Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的Lambda ,给我们操作集合(Collection)提供了极大的便利。Stream流是JDK8新增的成员,允许以声明性方式处理数据集合,可以把Stream流看作是遍历数据集合的一个高级迭代器。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找/筛选/过滤、排序、聚合和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1、使用流的好处
代码以声明性方式书写,说明想要完成什么,而不是说明如何完成一个操作。
可以把几个基础操作连接起来,来表达复杂的数据处理的流水线,同时保持代码清晰可读。

2、流是什么?
从支持数据处理操作的源生成元素序列.数据源可以是集合,数组或IO资源。

从操作角度来看,流与集合是不同的. 流不存储数据值; 流的目的是处理数据,它是关于算法与计算的。

如果把集合作为流的数据源,创建流时不会导致数据流动; 如果流的终止操作需要值时,流会从集合中获取值; 流只使用一次。

流中心思想是延迟计算,流直到需要时才计算值。
 

Stream可以由数组或集合创建,对流的操作分为两种:

中间操作,每次返回一个新的流,可以有多个。

终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

特性:

不是数据结构,不会保存数据。

不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)

惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。

  • 无状态:指元素的处理不受之前元素的影响;
  • 有状态:指该操作只有拿到所有元素之后才能继续下去。
  • 非短路操作:指必须处理所有元素才能得到最终结果;
  • 短路操作:指遇到某些符合条件的元素就可以得到最终结果,如 A || B,只要A为true,则无需判断B的结果。

三、Stream的创建

Stream可以通过集合数组创建。

1、Collection.stream()

通过 java.util.Collection.stream() 方法用集合创建流

List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
12345

2、Arrays.stream(T[]array)
使用 java.util.Arrays.stream(T[]array)方法用数组创建流

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
12

3、of()、iterate()、generate()

使用 Stream的静态方法:of()、iterate()、generate()

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
 
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println);
 
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
1234567

stream和 parallelStream的简单区分:stream是顺序流,由主线程按顺序对流执行操作,而 parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:

如果流中的数据量足够大,并行流可以加快处速度。

除了直接创建并行流,还可以通过 parallel()把顺序流转换成并行流:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

 

五、Stream API 案例

以下为案例的测试数据

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
public class PersonTest {
    static List<Person> personList = new ArrayList<Person>();

    private static void initPerson() {
        /*personList.add(new Person("张三", 8, 3000));
        personList.add(new Person("李四", 18, 5000));
        personList.add(new Person("王五", 28, 7000));
        personList.add(new Person("孙六", 38, 9000));*/

        personList.add(new Person("张三", 25, 3000, "male", "上海"));
        personList.add(new Person("李四", 27, 5000, "male", "上海"));
        personList.add(new Person("王五", 29, 7000, "female", "上海"));
        personList.add(new Person("孙六", 26, 3000, "female", "北京"));
        personList.add(new Person("王伟", 27, 5000, "male", "北京"));
        personList.add(new Person("张丽", 21, 7000, "female", "北京"));

    }

    public static void main(String[] args) {
        initPerson();
    }
}

class Person {
    String name;
    int age;
    int salary;
    String sex;
    String area;

    public Person(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Person(String name, int age, int salary, String sex, String area) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.sex = sex;
        this.area = area;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}

1、遍历、匹配

list.stream().forEach(System.out::println);


1
// 匹配第一个值

Integer first = list1.stream().findFirst().get();
// 匹配任意一个值(适用于并行流)
Integer any = list1.parallelStream().findAny().get();
// 是否包含符合特定条件的元素
boolean b = list1.stream().anyMatch(i -> i > 3);

2、过滤

List<Integer> newList = list1.stream().filter(i -> i > 3).collect(Collectors.toList());

3、聚合(最大、最小、计数)
(1)最大值 max
// 数字集合
 

List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
Integer max = numList.stream().max(Comparator.comparingInt(i -> i)).get();

// 使用 IntStream
int max = personList.stream().mapToInt(Person::getSalary).max().getAsInt();

// 对象集合
Person maxPerson = personList.stream().max(Comparator.comparingInt(Person::getSalary)).get();

(2)最小值 min

// 自定义比较器
Person minPerson = personList.stream().min((v1, v2) -> v1.getSalary() - v2.getSalary()).get();

(3)计数 count

// 统计集合中元素的个数
long count = numList.stream().count();

4、peek、map、flatMap
peek:是个中间操作,它提供了一种对流中所有元素操作的方法。生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;但一般不推荐使用,文档提示:该方法主要用于调试,做一些消耗这个对象但不修改它的东西(发送通知,打印模型等)。

 public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("1", "tom");
        map.put("2", "jerry");

        // 将map中的value转大写
        map = map.entrySet()//获取集合
                .stream()//获取流
                .peek(obj -> obj.setValue(obj.getValue().toUpperCase(Locale.ROOT)))//peek支持在每个元素上执行一个操作并且返回新的stream
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));//collect方法用来将流转到集合对象
        //遍历输出
        map.forEach((key, value) -> System.out.println(key + ":" + value));
    }


map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
这个方法有三个对于原始类型的变种方法,分别 是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新 的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;

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

// 对集合中每个数字加3
List<Integer> newList = numList.stream().map(i -> i + 3).collect(Collectors.toList());

List<String> nameList = Arrays.asList("张三", "李四");
// 取出流中的姓名,将姓取出转换为流返回,最后将返回流合并为一个
List<Character> surNameList = nameList.stream().flatMap(s -> Stream.of(s.charAt(0))).collect(Collectors.toList());
System.out.println(surNameList);

5、规约 reduce

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作

// 求和案例
list.stream().reduce((x, y) -> {
    System.out.println("x=" + x + ",y=" + y);
    return x + y;
});


x=1,y=2
x=3,y=3
x=6,y=4
// 求积
Integer product = list.stream().reduce((x, y) -> x * y).get();
// 求最大值
Integer max = list.stream().reduce((x, y) -> x > y ? x : y).get();
// 求最大值
Integer max = list.stream().reduce(Integer::max).get();

求所有员工的工资之和和最高工资

Integer sum = personList.stream().map(Person::getSalary).reduce(Integer::sum).get();
Integer max = personList.stream().map(Person::getSalary).reduce(Integer::max).get();

6、收集(toList、toSet、toMap)

(1)toMap

// 将list转为map
Map<String, Person> personMap = personList.stream().collect(Collectors.toMap(Person::getName, p -> p));
System.out.println(personMap);

(2)toCollection

使用toCollection可以自定义转换后的集合对象。

// 将list转换set
Set personSet = personList.stream().collect(Collectors.toCollection(HashSet::new));
// 将set转换list
LinkedList list = personSet.stream().collect(Collectors.toCollection(LinkedList::new));

(3)数据去重案例

// 根据区域进行去重

ArrayList<Person> list = personList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getArea))), ArrayList::new));

// 去重结果
[Person{name='张三', age=25, salary=3000, sex='male', area='上海'}, Person{name='孙六', age=26, salary=3000, sex='female', area='北京'}]

7、collect
(1)统计
Collectors提供了一系列用于数据统计的静态方法:

计数: count

平均值: averagingInt、 averagingLong、 averagingDouble

最值: maxBy、 minBy

求和: summingInt、 summingLong、 summingDouble

统计以上所有: summarizingInt、 summarizingLong、 summarizingDouble
 

// 平均
Double avg = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));

// 最大
Integer max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare)).get();

// 求和
Long sum = personList.stream().collect(Collectors.summingLong(Person::getSalary));

// 一次统计所有信息(DoubleSummaryStatistics{count=4, sum=24000.000000, min=3000.000000, average=6000.000000, max=9000.000000})
DoubleSummaryStatistics statistics = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println(statistics);

(2)map转list

map.entrySet().stream().map(e -> new Person(e.getKey(),e.getValue())).collect(Collectors.toList());
map.keySet().stream().collect(Collectors.toList());
map.values().stream().collect(Collectors.toList());

3)list转map
// list转换成map,并指定key重复时的覆盖规则,否则重复key会报错。

Map<String, Person> personMap = personList.stream().collect(Collectors.toMap(Person::getName, Function.identity(), (key1, key2) -> key2));

// list转换成map,指定具体的实现map
Map<String, Person> personMap = personList.stream().collect(Collectors.toMap(Person::getName, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));

(4)两个list转map
可变汇聚对应的只有一个方法:collect,正如其名字显示的,它可以把Stream中的要有元素收集到一个结果容器中(比如Collection)。先看一下最通用的collect方法的定义(还有其他override方法):

<R> R collect(Supplier<R> supplier,
              ObjIntConsumer<R> accumulator,
              BiConsumer<R, R> combiner);

先来看看这三个参数的含义:Supplier supplier是一个工厂函数,用来生成一个新的容器;BiConsumer accumulator也是一个函数,用来把Stream中的元素添加到结果容器中;BiConsumer combiner还是一个函数,用来把中间状态的多个结果容器合并成为一个(并发的时候会用到)。看晕了?来段代码!

String[] names = {"张三", "李四"};
int[] scores = {80, 90};
// 单线程
HashMap<Object, Object> map = IntStream.range(0, names.length).collect(HashMap::new, (m, i) -> m.put(names[i], scores[i]), null);
System.out.println(map);

// 多线程
HashMap<Object, Object> map2 = IntStream.range(0, names.length).parallel().collect(HashMap::new, (m, i) -> m.put(names[i], scores[i]), (m1, m2)->m1.putAll(m2));
System.out.println(map2);

8、分组
 

// 根据布尔值分组
Map<Boolean, List<Person>> booleanMap = personList.stream().collect(Collectors.partitioningBy(p -> p.getSalary() > 5000));

// 根据性别分组
Map<String, List<Person>> sexMap = personList.stream().collect(Collectors.groupingBy(p -> p.getSex()));

// 先根据性别分组,再根据地区分组
Map<String, Map<String, List<Person>>> sexAreaMap = personList.stream().collect(Collectors.groupingBy(p -> p.getSex(), Collectors.groupingBy(p -> p.getArea())));

// 分组计数(键为元素,值为元素出现的次数)
Map<String, Long> sexCountMap = personList.stream().collect(Collectors.groupingBy(p -> p.getSex(), Collectors.counting()));

// 分组求和
Map<String, Integer> sumMap = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.summingInt(Person::getSalary)));



9、连接 joining

// joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
// 张三,李四,王五,孙六,王伟,张丽
System.out.println(names);

10、排序 sorted

// 按工资排序(升序)
List<Person> sorted = personList.stream().sorted(Comparator.comparing(Person::getSalary)).collect(Collectors.toList());

// 按工资排序(降序)
personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).collect(Collectors.toList());

// 先按工资,再按年龄排序
List<Person> personList = PersonTest.personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).collect(Collectors.toList());

// 工资降序,年龄升序
List<Person> personList = PersonTest.personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed().thenComparing(Person::getAge)).collect(Collectors.toList());

// 工资升序,年龄降序
List<Person> personList = PersonTest.personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing((x, y) -> y.getAge() - x.getAge())).collect(Collectors.toList());

11、提取、组合

流也可以进行合并、去重、限制、跳过等操作。

String[] arr1 = {"a", "b", "c", "d"};
String[] arr2 = {"d", "e", "f", "g"};
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// concat:合并两个流 distinct:去重
List<String> concatList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
// limit:限制从流中获得前n个数据
List<Integer> limitList = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
// skip:跳过前n个数据
List<Integer> skipList = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

System.out.println("流合并:" + concatList);
System.out.println("limit:" + limitList);
System.out.println("skip:" + skipList);



12、读取文件流

File file = new File("F:\\filetest.txt");
// 代码放入try()中可以自动关闭流
try (Stream<String> stream = Files.lines(file.toPath(), StandardCharsets.UTF_8)) {
    stream.onClose(() -> System.out.println("done!")).forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}


13、计算差集

List<String> list1 = Arrays.asList("a", "b", "c", "d");
List<String> list2 = Arrays.asList("d", "e", "f", "g");
List<String> list = list1.stream().filter(s -> !list2.contains(s)).collect(Collectors.toList());
System.out.println(list);


六、参考
Java8中Stream详细用法大全
https://blog.csdn.net/qq_45228323/article/details/121328790

深入理解Java Stream流水线
https://www.cnblogs.com/CarpenterLee/p/6637118.html

深入理解 Java 8 Lambda(类库篇 - Streams API,Collectors 和并行)
https://juejin.cn/post/6844903458131148813
 

整体代码:

StreamTest:
package com.cn.jettech.jettoproimage.controller.imagecontroller01.imagecontroller01.lambda;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTest {
    public  static List<Person> personList = new ArrayList<Person>();
    public static HashMap<Integer,Person> personMap = new HashMap<Integer,Person>();
    private static void initPerson() {
//        personList.add(new Person("张三", 8, 3000));
//        personList.add(new Person("李四", 18, 5000));
//        personList.add(new Person("王五", 28, 7000));
//        personList.add(new Person("孙六", 38, 9000));

        personList.add(new Person("张三", 25, 3000, 3021L, 3002.00, "male", "上海"));
        personList.add(new Person("李四", 27, 5000, 5001L,5002.00,  "male", "上海"));
        personList.add(new Person("王五", 20, 7000, 70001L,70002.00,"female", "上海"));
        personList.add(new Person("孙六", 25, 3000, 3001L, 3002.00, "female", "北京"));
        personList.add(new Person("王伟", 27, 5000, 5001L,5002.00,   "male", "北京"));
        personList.add(new Person("张丽", 30, 7000, 70001L,70002.00, "female", "北京"));

        personMap.put(1,new Person("张三", 25, 3020, 3021L, 3002.00, "male", "上海"));
        personMap.put(2,new Person("李四", 27, 5000, 5001L,5002.00,  "male", "上海"));
        personMap.put(3,new Person("王五", 20, 7000, 70001L,70002.00,"female", "上海"));
        personMap.put(4,new Person("孙六", 26, 3000, 3001L, 3002.00, "female", "北京"));
        personMap.put(5,new Person("王伟", 27, 5000, 5001L,5002.00,   "male", "北京"));
        personMap.put(6,new Person("张丽", 30, 7000, 70001L,70002.00, "female", "北京"));
    }
    public static void main(String[] args) {
        initPerson();
        //personList.stream().forEach(System.out::println);
        //System.out.println(personList.stream().findFirst().get());
        //System.out.println(personList.stream().allMatch(i->i.getAge()>26));
        //System.out.println(personList.stream().anyMatch(i->i.getAge()>26));
        //personList.stream().filter(i->i.getAge()>25).forEach(System.out::println);
        //System.out.println(personList.stream().filter(i->i.getAge()>25).collect(Collectors.toList()));
        //System.out.println(personList.stream().mapToDouble(Person::getSalaryD).max().getAsDouble());
        //System.out.println(personList.stream().max(Comparator.comparingInt(Person::getAge)).get());
        //System.out.println(personList.stream().min((v1,v2)->v1.getAge() - v2.getAge()).get());
//        System.out.println(personList.stream().map(i-> {
//            i.setAge(i.getAge()+3);
//            return i;
//        }).collect(Collectors.toList()));
    //    System.out.println(personList.stream().flatMap(s-> Stream.of(s.getAge()+3)).collect(Collectors.toList()));
         //System.out.println(personList.stream().reduce((v1,v2)->{v1.setAge(v2.salary);return v1;}).get());
        //System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::sum).get());
        //自己写reduce逻辑
        //System.out.println(personList.stream().map(Person::getName).reduce((v1,v2)->v1+v2).get());
        //System.out.println(personList.stream().collect(Collectors.toMap(Person::getName,p->p)));
//        Set set = personList.stream().collect(Collectors.toCollection(HashSet::new));
//        System.out.println(set);
        //LinkedList list =personList.stream().collect(Collectors.toCollection(LinkedList::new));
        //System.out.println(list);
        //去重复
//        ArrayList list = personList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()->new TreeSet<>(Comparator.comparing(Person::getArea))),ArrayList::new));
//        System.out.println(list);
        //System.out.println(personList.stream().collect(Collectors.averagingInt(Person::getAge)));
 //        System.out.println(personList.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compare)).get());
        //System.out.println(personList.stream().collect(Collectors.summarizingDouble(Person::getSalary)));

        //System.out.println(personMap.keySet().stream().collect(Collectors.toList()));
        //System.out.println(personMap.values().stream().collect(Collectors.toList()));
        //System.out.println(personMap.entrySet().stream().map(e->new Person(e.getKey(),e.getValue())).collect(Collectors.toList()));
        //System.out.println(personList.stream().collect(Collectors.toMap(Person::getName,Function.identity(),(k1,k2)->k1)));
        //System.out.println(personList.stream().collect(Collectors.groupingBy(p->p.getSex(),Collectors.groupingBy(p->p.getSalary()))));
       // System.out.println(personList.stream().collect(Collectors.groupingBy(p->p.getSex(),Collectors.counting())));
        //System.out.println(personList.stream().collect(Collectors.groupingBy(p->p.getSex(),Collectors.summarizingDouble(Person::getSalary))));
       // System.out.println(personList.stream().map(Person::getName).collect(Collectors.joining(",")));
       // System.out.println(personList.stream().sorted(Comparator.comparingInt(Person::getSalary).reversed().thenComparing(Person::getAge).reversed()).collect(Collectors.toList()));

//        String[] arr1 = {"a", "b", "c", "d"};
//        String[] arr2 = {"d", "e", "f", "g"};
//        Stream<String> stringStream1 = Stream.of(arr1);
//        Stream<String> stringStream2 = Stream.of(arr2);
//        System.out.println(Stream.concat(stringStream1,stringStream2).distinct().collect(Collectors.toList()));
//        System.out.println(Stream.iterate(0,x->x+3).limit(10).collect(Collectors.toList()));
        //  System.out.println(Stream.iterate(1,x->x+2).skip(1).limit(5).collect(Collectors.toList()));

//        File file = new File("G:\\work\\a.txt");
//        try {
//            Stream<String>  stringStream = Files.lines(file.toPath(), StandardCharsets.UTF_8);
//            stringStream.onClose(()-> System.out.println("done")).forEach(System.out::println);
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
        List<String> list1 = Arrays.asList("a", "b", "c", "d");
        List<String> list2 = Arrays.asList("d", "e", "f", "g");
        System.out.println(list1.stream().filter(s->!list2.contains(s)).collect(Collectors.toList()));


    }
}
Person:
package com.cn.jettech.jettoproimage.controller.imagecontroller01.imagecontroller01.lambda;

public class Person {
    public int  num;
    public  Person person;
    public  String name;
    public  int age;
    public  int salary;
    public  Long salaryL;
    public  Double salaryD;
    public String sex;
    public String area;

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", person=" + person +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", salaryL=" + salaryL +
                ", salaryD=" + salaryD +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Person getPerson() {
        return person;
    }

    public void setPerson(Person person) {
        this.person = person;
    }

    public Person(int num, Person person){
        this.num = num;
        this.person = person;
    }

    public Long getSalaryL() {
        return salaryL;
    }

    public void setSalaryL(Long salaryL) {
        this.salaryL = salaryL;
    }

    public Double getSalaryD() {
        return salaryD;
    }

    public void setSalaryD(Double salaryD) {
        this.salaryD = salaryD;
    }

    public Person(String name, int age, int salary, Long salaryL, Double salaryD, String sex, String area) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.salaryL = salaryL;
        this.salaryD = salaryD;
        this.sex = sex;
        this.area = area;
    }

    public Person(String name, int age, int salary, String sex, String area) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.sex = sex;
        this.area = area;
    }

    public Person(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值