JDK8-Stream

2020-09-10  本文已影响0人  张明学

Stream流有一些特性

使用Stream流,可以清楚地知道我们要对一个数据集做何种操作,可读性强。而且可以很轻松地获取并行化Stream流,不用自己编写多线程代码,可以让我们更加专注于业务逻辑。

怎么创建Stream类?

四种方式:

Stream<String> stringSteam = Stream.of("a", "bb", "ccc", "dddd", "abc", "abcd");
//输出:a,bb,ccc,dddd,abc,abcd
Stream stringSteam = Stream.builder().add("a").add("bb").add("ccc").build();
//输出:a,bb,ccc
Stream stringSteam = Stream.iterate(0, x -> x + 1).limit(3);
//输出:0,1,2
Stream<String> generate = Stream.generate(() -> "hello world").limit(3);
// 输出三个“hello world”

另外:数组可以通过Arrays类中的stream(...)方法转换成Stream,集合可以通过Collection接口中的stream()方法转换成Stream。

tring[] strs = new String[]{"a", "bb", "ccc"};
Stream<String> stream = Arrays.stream(strs);

List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("bb");
strList.add("ccc");
Stream<String> stream = strList.stream();

使用Stream类进行数据操作

allMatch

全部匹配

/**
 * allMatch- 全部匹配
 */
@Test
public void testAllMatch() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
    boolean isAllMatch = stringSteam.allMatch(item -> {
        return item.contains("b");
    });
    System.out.println(isAllMatch);
}

结果:true

Filter

过滤操作,返回Stream,可以进行链式调用


stream-filter.png
/**
 * Filter 过滤操作,返回Stream,可以进行链式调用
 */
@Test
public void testFilter() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
    Stream filterStream = stringSteam.filter(item -> {
        return item.contains("b");
    });
    filterStream.forEach(System.out::println);
}

结果:bd abc abcd

Map

对Stream里的元素都做一下映射操作,返回Stream


stream-map.png
/**
 * Map -对Stream里的元素都做一下映射操作,返回Stream
 */
@Test
public void testMap() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
    Stream mapSteam = stringSteam.map(item -> {
        return item.toUpperCase();
    });
    mapSteam.forEach(System.out::println);
}

结果:BD ABC ABCD

flatMap

对Stream里的元素都做一下映射操作,元素映射操作返回的是Stream,最终返回Stream


stream-flatMap.png
/**
 * flatMap - 对Stream里的元素都做一下映射操作,元素映射操作返回的是Stream,总终返回Stream
 */
@Test
public void testFlatMap() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
    Stream mapSteam = stringSteam.flatMap(item -> {
        return Arrays.stream(item.split(""));
    });
    mapSteam.forEach(System.out::println);
}

结果:b d a b c a b c d

concat

流连接操作

/**
 * concat - 流连接操作
 */
@Test
public void testConcat() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
    Stream.concat(Stream.of("bd", "abc"), Stream.of("e", "f")).forEach(System.out::println);
    ;
}

peek

生成一个包含原Stream的所有元素的新Stream,新Stream每个元素被消费之前都会执行peek给定的消费函数

/**
 * peek-生成一个包含原Stream的所有元素的新Stream,新Stream每个元素被消费之前都会执行peek给定的消费函数
 */
@Test
public void testPeek() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd");
    Stream<String> resultSteam = stringSteam.peek(item -> {
        System.out.println("消费item->" + item);
    });
    // resultSteam在执行forEach里的Consumer之前时才会执行peek(惰性)
    resultSteam.forEach(System.out::println);
}

skip

跳过前N个元素后,剩下的元素重新组成一个Stream

/**
 * skip 跳过前N个元素后,剩下的元素重新组成一个Stream
 */
@Test
public void testSkip() {
    Stream<String> stringSteam = Stream.of("bd", "abc", "abcd").skip(1);
    stringSteam.forEach(System.out::println);
}

max, min

最大最小值

/**
 * 最大最小值max, min
 */
@Test
public void testMax() {
    Optional<String> optional = Stream.of("bd", "abc", "abcd").max((item1, item2) -> {
        return Integer.valueOf(item1.length()).compareTo(Integer.valueOf(item2.length()));
    });
    System.out.println(optional.get());
}

reduce

累积器,将流中的元素两个一处理,返回结果


stream-reduce.png
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

第一个API的示例:

@Test
public void testReduce1() {
    /**
     * 两个元素相处理
     */
    Optional<String> optional = Stream.of("bd", "abc", "abcd").reduce((item1, item2) -> {
        System.out.println("item1=" + item1 + " item2=" + item2);
        return item1 + "_" + item2;
    });
    System.out.println(optional.get());
}

结果:

item1=bd item2=abc
item1=bd_abc item2=abcd
bd_abc_abcd

第二个API就是在第一个API基础上加一个初始值

@Test
public void testReduce2() {
    String optiona2 = Stream.of("bd", "abc", "abcd").reduce("init", (item1, item2) -> {
        return item1 + "_" + item2;
    });
    System.out.println(optiona2);
}

结果:init_bd_abc_abcd

第三个API需要在并行的Stream中使用

@Test
public void testReduce3() {
    List<String> itemList = new ArrayList<>();
    itemList.add("bd");
    itemList.add("abc");
    itemList.add("abcd");
    String optiona3 = itemList.parallelStream().reduce(
            // 初始值
            "init",
            // 累加处理
            (item1, item2) -> {
                return item1 + "__" + item2;
            },
            // 将行并累计结果再合并
            (e1, e2) -> {
                return e1 + "##" + e2;
            });
    System.out.println(optiona3);
}

结果:init__bd##init__abc##init__abcd

Collect

暂时还没想好中文怎么解释...有两个API

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

第一个API主要用Collector
JDK8-Stream-Collectors

@Test
public void testCollect1() {
    List<String> resutList = Stream.of("bd", "abc", "abcd").collect(Collectors.toList());
    System.out.println(resutList);
}

第二个API的理解是:

第一个参数supplier 是一个生成目标类型实例的方法,第二个参数accumulator 将目标数据填充到第一步中,第三个参数combiner合并多个第二步的结果(只有并行Steam中生效)

@Test
public void testCollect2() {

    /**
     * 第一个参数supplier 是一个生成目标类型实例的方法,代表着目标容器是什么。
     * 第二个参数accumulator 是将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中;
     * 第三个参数combiner是将多个supplier生成的实例整合到一起的方法,代表着规约操作,将多个结果合并
     */
    List<Person> listPerson = new ArrayList<>();
    listPerson.add(new Person(10, "张三"));
    listPerson.add(new Person(12, "李四"));
    listPerson.add(new Person(14, "王五"));

    Map<Long, Person> personMap = listPerson.stream().collect(
            // 生成目标类型实例的方法
            () -> {
                return new HashMap<>();
            },
            // 将目标数据填充到第一步中
            (hashMap, p) -> {
                hashMap.put(p.getId(), p);
            },
            // 本例不是并行的stream,该方法没有实际意义
            (hashMap, n) -> {
                hashMap.putAll(n);
            });

    System.out.println(personMap);
}

输出:{10=Person(id=10, name=张三), 12=Person(id=12, name=李四), 14=Person(id=14, name=王五)}

@Test
public void testCollect3() {

    /**
     * 第一个参数supplier 是一个生成目标类型实例的方法,代表着目标容器是什么。
     * 第二个参数accumulator 是将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中;
     * 第三个参数combiner是将多个supplier生成的实例整合到一起的方法,代表着规约操作,将多个结果合并(并行操作中生效parallelStream)
     */
    List<Person> listPerson = new ArrayList<>();
    listPerson.add(new Person(10, "张三"));
    listPerson.add(new Person(12, "李四"));
    listPerson.add(new Person(14, "王五"));

    Map<Long, Person> personMap = listPerson.parallelStream().collect(
            // 生成目标类型实例的方法
            () -> {
                return new HashMap<>();
            },
            // 将目标数据填充到第一步中
            (hashMap, p) -> {
                hashMap.put(p.getId(), p);
            },
            // 将多个结果合并
            (hashMap, n) -> {
                hashMap.putAll(n);
            });

    System.out.println(personMap);
}

输出日志:

create 容器 HashMap
create 容器 HashMap
create 容器 HashMap
并且容器HashMap
并且容器HashMap
{10=Person(id=10, name=张三), 12=Person(id=12, name=李四), 14=Person(id=14, name=王五)}

除了Stream类,还有IntStream、DoubleStream、LongStream,很显然这三种都是有针对性的流操作,分别针对int、double、long类型的数据,它们都继承BaseStream类。

上一篇下一篇

猜你喜欢

热点阅读