Java8 特性笔记(四) Stream
上一章节,我们简单的介绍了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相加,这样循环下去,相比大家有疑问,难道这个就只能做相加吗,肯定不是啦,这里只是表达这样一个顺序,里面要做什么事,还是我们自己去定义的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)