Stream是Java 8 API添加的一个新的抽象,称为流Stream,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)
Stream流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算
Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。
1、代码简洁:函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环
2、多核友好:Java函数式编程使得编写并行程序如此简单,就是调用一下方法
1、将集合转换为Stream流(或者创建流)
2、操作Stream流(中间操作,终端操作)
stream流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果


BaseStream:基础接口,声明了流管理的核心方法;
Stream:核心接口,声明了流操作的核心方法,其他接口为指定类型的适配
生成流的方式主要有五种
Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.add(4);
integerList.add(5);
Stream<Integer> listStream = integerList.stream();
int[] intArr = {1, 2, 3, 4, 5};
IntStream arrayStream = Arrays.stream(intArr);
通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是 Stream
注:
使用数值流可以避免计算过程中拆箱装箱,提高性能。
Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流
try {
Stream<String> fileStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}
通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(5);
iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
Stream<Double> generateStream = Stream.generate(Math::random).limit(5);
generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断
流的操作类型主要分为两种:中间操作符、终端操作符
通常对于Stream的中间操作,可以视为是源的查询,并且是懒惰式的设计,对于源数据进行的计算只有在需要时才会被执行,与数据库中视图的原理相似;
Stream流的强大之处便是在于提供了丰富的中间操作,相比集合或数组这类容器,极大的简化源数据的计算复杂度
一个流可以跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用
这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有下面即将介绍的 filter、map 等
| 流方法 | 含义 | 示例 |
|---|---|---|
| filter | 用于通过设置的条件过滤出元素 | List strings = Arrays.asList(“abc”, “”, “bc”, “efg”, “abcd”,"", “jkl”); List filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); |
| map | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”) | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”); List mapped = strings.stream().map(str->str+"-itcast").collect(Collectors.toList()); |
| distinct | 返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流 | List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println); |
| sorted | 返回排序后的流 | List strings1 = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); List sorted1 = strings1.stream().sorted().collect(Collectors.toList()); |
| limit | 会返回一个不超过给定长度的流 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”); List limited = strings.stream().limit(3).collect(Collectors.toList()); |
| skip | 返回一个扔掉了前n个元素的流 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”); List skiped = strings.stream().skip(3).collect(Collectors.toList()); |
| flatMap | 使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”); Stream flatMap = strings.stream().flatMap(Java8StreamTest::getCharacterByString); |
| peek | 对元素进行遍历处理 | List strings = Arrays.asList(“abc”, “abc”, “bc”, “efg”, “abcd”,“jkl”, “jkl”); strings .stream().peek(str-> str + "a").forEach(System.out::println); |
public static void main(String[] args) {
List<User> userList = getUserList();
}
private static List<User> getUserList() {
List<User> userList = new ArrayList<>();
userList.add(new User(1,"张三",18,"上海"));
userList.add(new User(2,"王五",16,"上海"));
userList.add(new User(3,"李四",20,"上海"));
userList.add(new User(4,"张雷",22,"北京"));
userList.add(new User(5,"张超",15,"深圳"));
userList.add(new User(6,"李雷",24,"北京"));
userList.add(new User(7,"王爷",21,"上海"));
userList.add(new User(8,"张三丰",18,"广州"));
userList.add(new User(9,"赵六",16,"广州"));
userList.add(new User(10,"赵无极",26,"深圳"));
return userList;
}
用于通过设置的条件过滤出元素
//1、filter:输出ID大于6的user对象
List<User> filetrUserList = userList.stream().filter(user -> user.getId() > 6).collect(Collectors.toList());
filetrUserList.forEach(System.out::println);
接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)
//2、map
List<String> mapUserList = userList.stream().map(user -> user.getName() + "用户").collect(Collectors.toList());
mapUserList.forEach(System.out::println);

返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流
//3、distinct:去重
List<String> distinctUsers = userList.stream().map(User::getCity).distinct().collect(Collectors.toList());
distinctUsers.forEach(System.out::println);

返回排序后的流
//4、sorted:排序,根据名字倒序
userList.stream().sorted(Comparator.comparing(User::getName).reversed()).collect(Collectors.toList()).forEach(System.out::println);
会返回一个不超过给定长度的流
//5、limit:取前5条数据
userList.stream().limit(5).collect(Collectors.toList()).forEach(System.out::println);

返回一个扔掉了前n个元素的流
//6、skip:跳过第几条取后几条
userList.stream().skip(7).collect(Collectors.toList()).forEach(System.out::println);
使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,即扁平化为一个流
//7、flatMap:数据拆分一对多映射
userList.stream().flatMap(user -> Arrays.stream(user.getCity().split(","))).forEach(System.out::println);
map:对流中每一个元素进行处理
flatMap:流扁平化,让你把一个流中的“每个值”都换成另一个流,然后把所有的流连接起来成为一个流
本质区别:map是对一级元素进行操作,flatmap是对二级元素操作map返回一个值;flatmap返回一个流,多个值
应用场景:map对集合中每个元素加工,返回加工后结果;flatmap对集合中每个元素加工后,做扁平化处理后(拆分层级,放到同一层)然后返回
对元素进行遍历处理
//8、peek:对元素进行遍历处理,每个用户ID加1输出
userList.stream().peek(user -> user.setId(user.getId()+1)).forEach(System.out::println);

Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流
一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。
终端操作的执行,才会真正开始流的遍历。如 count、collect 等
| 流方法 | 含义 | 示例 |
|---|---|---|
| collect | 收集器,将流转换为其他形式 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); Set set = strings.stream().collect(Collectors.toSet()); List list = strings.stream().collect(Collectors.toList()); Map<String, String> map = strings.stream().collect(Collectors.toMap(v ->v.concat("_name"), v1 -> v1, (v1, v2) -> v1)); |
| forEach | 遍历流 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”);strings.stream().forEach(s -> out.println(s)); |
| findFirst | 返回第一个元素 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); Optional first = strings.stream().findFirst(); |
| findAny | 将返回当前流中的任意元素 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); Optional any = strings.stream().findAny(); |
| count | 返回流中元素总数 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); long count = strings.stream().count(); |
| sum | 求和 | int sum = userList.stream().mapToInt(User::getId).sum(); |
| max | 最大值 | int max = userList.stream().max(Comparator.comparingInt(User::getId)).get().getId(); |
| min | 最小值 | int min = userList.stream().min(Comparator.comparingInt(User::getId)).get().getId(); |
| anyMatch | 检查是否至少匹配一个元素,返回boolean | List strings = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); boolean b = strings.stream().anyMatch(s -> s == “abc”); |
| allMatch | 检查是否匹配所有元素,返回boolean | List strings = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); boolean b = strings.stream().allMatch(s -> s == “abc”); |
| noneMatch | 检查是否没有匹配所有元素,返回boolean | List strings = Arrays.asList(“abc”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); boolean b = strings.stream().noneMatch(s -> s == “abc”); |
| reduce | 可以将流中元素反复结合起来,得到一个值 | List strings = Arrays.asList(“cv”, “abd”, “aba”, “efg”, “abcd”,“jkl”, “jkl”); Optional reduce = strings.stream().reduce((acc,item) -> {return acc+item;});if(reduce.isPresent())out.println(reduce.get()); |
收集器,将流转换为其他形式
//1、collect:收集器,将流转换为其他形式
Set set = userList.stream().collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("--------------------------");
List list = userList.stream().collect(Collectors.toList());
list.forEach(System.out::println);
遍历流
//2、forEach:遍历流
userList.stream().forEach(user -> System.out.println(user));
userList.stream().filter(user -> "上海".equals(user.getCity())).forEach(System.out::println);
返回第一个元素
//3、findFirst:返回第一个元素
User firstUser = userList.stream().findFirst().get();
User firstUser1 = userList.stream().filter(user -> "上海".equals(user.getCity())).findFirst().get();
将返回当前流中的任意元素
//4、findAny:将返回当前流中的任意元素
User findUser = userList.stream().findAny().get();
User findUser1 = userList.stream().filter(user -> "上海".equals(user.getCity())).findAny().get();
返回流中元素总数
//5、count:返回流中元素总数
long count = userList.stream().filter(user -> user.getAge() > 20).count();
System.out.println(count);
求和
//6、sum:求和
int sum = userList.stream().mapToInt(User::getId).sum();
最大值
//7、max:最大值
int max = userList.stream().max(Comparator.comparingInt(User::getId)).get().getId();
最小值
//8、min:最小值
int min = userList.stream().min(Comparator.comparingInt(User::getId)).get().getId();
检查是否至少匹配一个元素,返回boolean
//9、anyMatch:检查是否至少匹配一个元素
boolean matchAny = userList.stream().anyMatch(user -> "北京".equals(user.getCity()));
检查是否匹配所有元素,返回boolean
//10、allMatch:检查是否匹配所有元素
boolean matchAll = userList.stream().allMatch(user -> "北京".equals(user.getCity()));
检查是否没有匹配所有元素,返回boolean
//11、noneMatch:检查是否没有匹配所有元素,返回boolean
boolean nonaMatch = userList.stream().allMatch(user -> "云南".equals(user.getCity()));
可以将流中元素反复结合起来,得到一个值
//12、reduce:将流中元素反复结合起来,得到一个值
Optional reduce = userList.stream().reduce((user, user2) -> {
return user;
});
if(reduce.isPresent()) System.out.println(reduce.get());
Collector:结果收集策略的核心接口,具备将指定元素累加存放到结果容器中的能力;并在Collectors工具中提供了Collector接口的实现类
将用户ID存放到List集合中
List<Integer> idList = userList.stream().map(User::getId).collect(Collectors.toList()) ;
将用户ID和Name以Key-Value形式存放到Map集合中
Map<Integer,String> userMap = userList.stream().collect(Collectors.toMap(User::getId,User::getName));
将用户所在城市存放到Set集合中
Set<String> citySet = userList.stream().map(User::getCity).collect(Collectors.toSet());
符合条件的用户总数
long count = userList.stream().filter(user -> user.getId()>1).collect(Collectors.counting());
对结果元素即用户ID求和
Integer sumInt = userList.stream().filter(user -> user.getId()>2).collect(Collectors.summingInt(User::getId)) ;
筛选元素中ID最小的用户
User maxId = userList.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getId))).get() ;
将用户所在城市,以指定分隔符链接成字符串;
String joinCity = userList.stream().map(User::getCity).collect(Collectors.joining("||"));
按条件分组,以城市对用户进行分组;
Map<String,List<User>> groupCity = userList.stream().collect(Collectors.groupingBy(User::getCity));
/**
* Return the value if present, otherwise return {@code other}.
*
* @param other the value to be returned if there is no value present, may
* be null
* @return the value, if present, otherwise {@code other}
* 返回值,如果存在,否则返回其他
*/
public T orElse(T other) {
return value != null ? value : other;
}
表示如果一个都没找到返回null(orElse()中可以塞默认值。如果找不到就会返回orElse中设置的默认值)
/**
* Return the value if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* @param other a {@code Supplier} whose result is returned if no value
* is present
* @return the value if present otherwise the result of {@code other.get()}
* @throws NullPointerException if value is not present and {@code other} is
* null
* 返回值如果存在,否则调用其他值并返回该调用的结果
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
表示如果一个都没找到返回null(orElseGet()中可以塞默认值。如果找不到就会返回orElseGet中设置的默认值)
orElse() 接受类型T的 任何参数,而orElseGet()接受类型为Supplier的函数接口,该接口返回类型为T的对象
1、当返回Optional的值是空值null时,无论orElse还是orElseGet都会执行
2、而当返回的Optional有值时,orElse会执行,而orElseGet不会执行
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class TestStream {
public static void main(String[] args) {
List<User> list = new ArrayList<>();
//定义三个用户对象
User user1 = new User();
user1.setUserName("admin");
user1.setAge(16);
user1.setSex("男");
User user2 = new User();
user2.setUserName("root");
user2.setAge(20);
user2.setSex("女");
User user3 = new User();
user3.setUserName("admin");
user3.setAge(18);
user3.setSex("男");
User user4 = new User();
user4.setUserName("admin11");
user4.setAge(22);
user4.setSex("女");
//添加用户到集合中
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
/*
在集合中查询用户名包含admin的集合
*/
List<User> userList = list.stream().filter(user -> user.getUserName().contains("admin")
&& user.getAge() <= 20).collect(Collectors.toList());
System.out.println(userList);
/*
在集合中查询出第一个用户名为admin的用户
*/
Optional<User> user = list.stream().filter(userTemp -> "admin".equals(userTemp.getUserName())).findFirst();
System.out.println(user);
/*
orElse(null)表示如果一个都没找到返回null(orElse()中可以塞默认值。如果找不到就会返回orElse中设置的默认值)
orElseGet(null)表示如果一个都没找到返回null(orElseGet()中可以塞默认值。如果找不到就会返回orElseGet中设置的默认值)
orElse()和orElseGet()区别:在使用方法时,即使没有值 也会执行 orElse 内的方法, 而 orElseGet则不会
*/
//没值
User a = list.stream().filter(userT-> userT.getAge() == 12).findFirst().orElse(getMethod("a"));
User b = list.stream().filter(userT11-> userT11.getAge() == 12).findFirst().orElseGet(()->getMethod("b"));
//有值
User c = list.stream().filter(userT2-> userT2.getAge() == 16).findFirst().orElse(getMethod("c"));
User d = list.stream().filter(userT22-> userT22.getAge() == 16).findFirst().orElseGet(()->getMethod("d"));
System.out.println("a:"+a);
System.out.println("b:"+b);
System.out.println("c:"+c);
System.out.println("d:"+d);
}
public static User getMethod(String name){
System.out.println(name + "执行了方法");
return null;
}
}

参考链接
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.
Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur