Java8 特性笔记(四) Stream

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jeekmary/article/details/88092388

上一章节,我们简单的介绍了Stream,这一节我们将继续介绍Stream的相关知识

1、创建方法


从集合中创建

private static Stream<String> cr5eateStreamFromCollection(){
	return list<String> Arrays.asList("hello","world","java");
}

从Stream的静态方法Stream.of中创建

private static Stream<String> cr5eateStreamFromStreamOf(){
	return Stream.of("hello","alex","java","python");
}

从数组中创建中创建

private static Stream<String> cr5eateStreamFromArrays(){
	String[] strings = {"hello", "world","stream"};
	return Arrays.stream(strings)
}

从文件中创建

private static Stream<String> cr5eateStreamFromPath(){
	Path path = Paths.get("c\\a.java");
	Stream<String> strteamFromFile = path.lines(path);
	streamFormFile.forEach(System.out::println);
	return streamFromFile;
}

从Iterator中创建

private static Stream<String> cr5eateStreamFromIterator(){
//这个创建方法会产生很多的数据,因此需要进行限制limit
Stream<Integer> stream = Stream.iterate(0, 0-n+2).limit(10);
	return stream ;
}

从Generate中创建Stream

private static Stream<Obj> createObjStreamFromGenerate() {
        return Stream.generate(new ObjSupplier()).limit(10);
    }

    static class ObjSupplier implements Supplier<Obj> {

        private int index = 0;

        private Random random = new Random(System.currentTimeMillis());

        @Override
        public Obj get() {
            index = random.nextInt(100);
            return new Obj(index, "Name->" + index);
        }
    }

    static class Obj {
        private int id;
        private String name;

        public Obj(int id, String name) {
            this.id = id;
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "Obj{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    '}';
        }
    }

stream的创建方法就这么多吗,肯定不是的,我们在实际开发中会发现Stream的创建方法非常之多,需要我们去积累。

2、操作符介绍

Stream属于管道里流,只能消费一次,第一个Stream流调用完毕,数据就会转到下一个Stream上,而这时第一个Stream流就不能再调用了

1、filter

可以通过filter方法,将一个流转化成另外一个流

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

通过上面的代码我们知道,filter这个操作符它接收的是一个Predcate对象,通过我们上面建的介绍可以知道Predicate是判断的意思,接收一个参数返回一个Boolean值
在这里插入图片描述
例子:

//这里代码作用是将偶数取出来
List<Integer> list =Arrays.asList(1,2,3,4,5,6,7,8,9);
List<Integer> result = list.stream().filter(i -> i%2 ==0).collect(toList());
// filter操作符后就是一个新的流
2、Map操作符

如果需要将流中的元素映射到另外一个流中,可以使用map方法

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

从上面的例子中我们可以看到该接口需要的是一个Function函数式接口,可以将当前流中的T类型转换成R类型的流,注意:Function是一个接口,接收一个参数,返回一个参数R appley(T t); map操作简称映射

例子:将字符串转换成int类型
//获取一个Stream类型的数据流

Stream<String> stream = Stream.of("1","2","3","4","5");
//使用map方法将字符串类型转换成整数
Stream<Integer> stream2  = stream.map((String a) -> Integer.parseInt(a));
stream2.forEach(i -> System.out.println(i)); 
//注意,这里我们根据函数推导可以简化写法
// String<Integer> stream2 = stream.map(Integer::parseInt);是不是超级舒服
3、统计个数方法 count

这个类似Collection中的size方法,统计其中元素的个数

long count();//注意这个方法返回的是long类型

提示: 这是一个终结方法,这个方法执行后,流就结束了,不想上面的方法,操作后返回的是另外一个流

例子:统计

Stream<String> original = Stream.of("a","b","c","d");
//这是一个中间方法,filter执行完后返回另外一个流
Stream<String> result = original.filter(s->s.equal("b"));
//这是一个终结方法,count执行完后,返回统计的个数,流终止
long result2 = result.count();
4、取用前几个: limit

limit方法可以对流进行截取,只去前面n个

Stream<T> limit<long maxSize>;

可见 limit方法也是一个中间方法,它不会终止流,而是返回一个流
在这里插入图片描述

例子:

Stream<String>  original = Stream.of("a","b","c","d","e","f","g");
Stream<String> result = original.limit(2);
result2 = result.count();//f返回2,这表示,limit是截取前面2个
5、跳过前几个元素 skip

如果希望跳过前面几个参数,可以使用skip方法,获取到一个截取之后的流

Stream<T> skip(long n)

如果流的当前长度大于N 则跳过前n个时会得到一个长度为0的空流
在这里插入图片描述
例子:

Strean<String> original = Stream.of("a","b","c","d","e","f","g");
Stream<String> result = original.skip(2);//跳过两个元素
System.out.println(resul.count())
5、组合方法: concat

如果有两个流,我们希望把两个流合并为一个流,那我们就可以使用Stream这个静态的方法 concat

static <T> Stream<T> concat(Stream<? extneds T> a, Stream<? extends T> b);

这是一个静态方法,这个方法个String中的caocat中的方法是不一样的

例子:

在这里插入代码片

Stream<String> streamA = Stream.of("a","c");
Stream<String> streamB = Stream.of("d","e");
Stream<String> result = Stream.concat(StreamA,StreamB);
6、扁平化方法: flatmap

flatmap是扁平化的意思,将数据流两级化成一级,例如将两个数组中的元素雷同的去掉,我们看下flatmap的源码:

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

可能大家对这个说的不是很好理解,我们先来看一种图
在这里插入图片描述
从上面我们可以看到,最先单词流的,也就是说一个单词一个元素,而使用flatmap元素之后变成了一个字符一个元素了,通过的讲就是把外面的壳子打开,里面的东西拿出来作为流

例子:

String[] words = {"hello","world"};
//{h,w,l,l,o} {w,o,r,l,d}
Stream<String[]> stream = Arrays.stream(words).map(w -> w.spilt(""));
//{h,w,l,l,o,w,o,r,l,d}
Stream<String>  stream2 = stream.flatmap(Arrays::stream)
stream2.distinct().forEach(System.out::println);
//这里 distinct 是去掉重复的元素
7、元素判断方法: allMatch

如果我们需要判断一组数中,是否所有的数都满足什么条件的时候,我们可以使用这个方法allMatch,我们来看下它的源码

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

我们不难发现,这方法传参也是Predicate,也就是说,最后返回的是一个Boolean值,也就是表达所有的数据是否满足某个条件

例子,判断一组数中是否全部满足大于2这个条件

        Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
        boolean match = stream.allMatch(i -> i > 2);
        System.out.println(match);
        //很明显返回的是falsel

当然,我们这里是求全部满足某个条件,那有没有只要满足一个就返回true的呢,肯定是有的,读者可以自己去研究哟,一样的

7、findAny的使用

从字面上看我们知道,是表示找到任意一个,在这里我们给大家扩充一点Optional的相关知识

Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//findAny 返回的是一个Optional
Optional<Integer> optional = stream.filter(i -> i%2 ==0).findAny();
//这里也就是拿到满足条件的一个值,毫无疑问这里肯定是能拿到值的
System.out.println(optional.get());//2
//find还有一个 findfirst命令,也就是按到满足条件的第一个
Optional<Integer> optional = stream.filter(i -> i%2 ==0).findFirst();

//再看另一种情况
Optional<Integer> optional = stream.filter(i -> i>100).findAny();
//这样的话如果直接get会怎么样呢,肯定会报异常的,因为没有数据可以拿
System.out.println(optional.get());//异常
//面对这样的情况怎么解决呢,我们一般的解决方案如下:

//定义一个方法,方法里面进入判断,传入参数Predicate<Integer> predicate
private static int find(Integet[] values, int defaultValue, Predicate<Integer> predicate){
	for(int i: values){
		if(predicate.test(i)){
			return i;
		}
	}
	return defaultValue;

}
//调用
       int result = find(new Integer[]{1, 2, 3, 4, 5, 6, 7}, -1, i -> i < 10);
        System.out.println(result);
// 当然这是一种方法,其实在上面的optional中有比较好的调用方式,下面这句话的意思也就使 如果找不到就返回-1,这样就避免空指针异常,是不是很简单呢
 System.out.println(optional3.orElse(-1));
8、reduce的使用

怎么理解 reduce 呢 reduce实际上是将流中的元素进行某种计算后返回一定的结果,我们一起来看下reduce的源码分析

    T reduce(T identity, BinaryOperator<T> accumulator);

BinaryOperator这个又是什么呢,我们继续看

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {

这下明白了,它是一个函数式接口,就是BiFunction,这个函数式接口我们前面介绍过,也就是传入两个参数,返回一个结果。我们看下源码的注释

**
	//下面的源码解释期,表明提供累计器,我们将元素累计起来返回,像求最大值最小值等
     * Performs a <a href="package-summary.html#Reduction">reduction</a> on the
     * elements of this stream, using the provided identity value and an
     * <a href="package-summary.html#Associativity">associative</a>
     * accumulation function, and returns the reduced value.  This is equivalent
     * to:
     * <pre>{@code
     *     T result = identity;
     *     for (T element : this stream)
     *         result = accumulator.apply(result, element)
     *     return result;
     * }</pre>
     *
     * but is not constrained to execute sequentially.
     *
     * <p>The {@code identity} value must be an identity for the accumulator
     * function. This means that for all {@code t},
     * {@code accumulator.apply(identity, t)} is equal to {@code t}.
     * The {@code accumulator} function must be an
     * <a href="package-summary.html#Associativity">associative</a> function.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal
     * operation</a>.
     *
     * @apiNote Sum, min, max, average, and string concatenation are all special
     * cases of reduction. Summing a stream of numbers can be expressed as:
     //这是给我们提供的例子,累加的例子
     * <pre>{@code
     *     Integer sum = integers.reduce(0, (a, b) -> a+b);
     * }</pre>
     *
     * or:
     //  使用函数式推导的写法
     * <pre>{@code
     *     Integer sum = integers.reduce(0, Integer::sum);
     * }</pre>
     *
     * <p>While this may seem a more roundabout way to perform an aggregation
     * compared to simply mutating a running total in a loop, reduction
     * operations parallelize more gracefully, without needing additional
     * synchronization and with greatly reduced risk of data races.
     *
     * @param identity the identity value for the accumulating function
     * @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
     *                    <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                    <a href="package-summary.html#Statelessness">stateless</a>
     *                    function for combining two values
     * @return the result of the reduction
     */

上面的第一个参数是初始值,如果没有就是0
下面这个是不带初始值的写法,这种下发返回的是Optional,所以可以使用ifParent方法

stream.reduce((i,j)->i+j).ifParent(System.out::println)

那这个ifParent又是做了什么呢? 我们看下源码信息

//原来非空判断
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

我在 java8 in Action这本书中找到这样一张图
在这里插入图片描述
从图中我们可以看到,这里有0作为初始值,将0和4相加得到4 ,然后又把4作为第一个值和5相加,这样循环下去,相比大家有疑问,难道这个就只能做相加吗,肯定不是啦,这里只是表达这样一个顺序,里面要做什么事,还是我们自己去定义的

好了,一些基本常见的操作符,我们就先介绍到这里,当然Stream的操作符很多,其他的需要我们在开发中去积累。

猜你喜欢

转载自blog.csdn.net/jeekmary/article/details/88092388
今日推荐