1.1、根据集合的创建
List list = new ArrayList<>();
Collections.addAll(list,"aa","aaa","Aaaa","b");//这段代码相当于使用了4次list.add
Stream stream = list.stream();
1.2、根据数组创建
//String数组
String[] strings = {"aa","aaa","Aaaa","b"};
Stream stringStream=Arrays.stream(strings);//第一种
Stream stringStream1=Stream.of(strings);//第二种
//Integer数组
Integer[] integers = {1,2,3,4,5,6};
Stream integerStream=Arrays.stream(integers);
Stream integerStream1=Stream.of(integers);//第二种
(下面两种创建方式不理解的话,先看完流操作,再回来看看)
1.3、根据文件创建
public static void main(String[] args) throws FileNotFoundException {
AtomicLong lineCount = new AtomicLong(0);//计算文件的行数
List stringList = new ArrayList<>();//用于接收读取的每一行内容,当size为一万时,存入数据库,然后清空
File file = new File(ResourceUtils.getURL("classpath:"+"test.txt").getPath());//我这是SpringBoot项目,此方法是获取resources目录下的test.txt,如果test.txt在resources\static下可以写成"classpath:"+"static/test.txt"
// File file = new File("F:"+File.separator+"test.txt");//也可以这样写,文件存在F:\test.txt
if (!file.exists()){
System.out.println("文件不存在!!");
return;
}
System.out.println(file.getPath());
try(BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
Stream stringStream=reader.lines();//得到流
stringStream.forEach(line -> {
System.out.println(line);
stringList.add(line);
lineCount.addAndGet(1);
if (lineCount.longValue()%10000==0){
//当stringList里有一万条数据时存一次数据库,然后清空stringList,这对于大小上G的文件读取很有用
}
});
}catch (IOException e) {
e.printStackTrace();
}
}
1.4、根据函数创建无限流
Stream API提供了两个静态方法创建无限流:Stream.iterate和Stream.generate,这样流的元素是无穷无尽的,一般用limit加以控制
1.4.1 Stream.iterate
Stream.iterate(0,n->n+2).limit(10).forEach(System.out::println);//将会从0开始打印十个双数
1.4.2 Stream.generate
Stream.generate(Math::random).limit(10).forEach(System.out::println);//输出十个0-1的随机双精度数
流操作分为:中间操作和终端操作
中间操作会返回另外一个流,而终端操作则会返回一个非流的结果如Integer、List、void(根据返回结果很容易区分是中间操作还是终端操作了)
因为中间操作返回的是一个流,所以还可以调用其他的中间操作,看起来就像是一条流水线,直到执行终端操作
利用代码来看一下,filter和map是中间操作,collect是终端操作
List list = new ArrayList<>();
Collections.addAll(list,"aa","aaa","Aaaa","b");
List list1=list.stream().filter(s -> s.contains("a")).map(s -> s.toLowerCase()).collect(toList());
//上面这段代码就像是:过滤出只包含“a”的内容->将上一次得到的内容转成小写->返回一个List的结果
//list1得到的数据是:[aa, aaa, aaaa]
下面我们会一一介绍中间操作和终端操作,并且都会有一个简单的使用的例子
//我们先看一下filter方法接收什么、下面是java.util.stream包下的Stream接口中的方法,这个filter到底是具体怎么实现的我们不需要管,我只需要知道这个方法接收什么类型的参数和返回时什么类型的参数。
//这里接收的是一个函数式接口Predicate(函数式接口:接口中有且只有一个抽象方法,他就是函数式接口,但是他可以有多个默认方法和静态方法),返回的是Stream返回的还是流所以filter是一个中间操作。
Stream filter(Predicate super T> predicate);
Stream涉及到许多函数式接口,所以我建议在学习Stream之前先学习一下java8的另外一个新特性Lambda.
Predicate中的抽象方法test接收的是泛型T,返回一个boolean值。
所以filter操作会接受一个返回boolean的函数作为参数,并且返回一个包含所有符合条件(函数返回true)的新流。
List list = new ArrayList<>();
Collections.addAll(list,"aa","aaa","Aaaa","b");
List list1=list.stream().filter(s -> s.length()>2).collect(toList());
这里我们从list中筛选出长度大于2的,符合条件的是“aaa”和“Aaaa”.
用于去除重复的内容
public static void main(String[] args) {
List list = new ArrayList<>();
Collections.addAll(list,"aaa","aaa","Aaaa","b");//有两个相同的“aaa”
List list1=list.stream().filter(s -> s.length()>2).distinct().collect(toList());//配合filter使用
System.out.println(list1);//打印[aaa, Aaaa],//去除重复的“aaa”,只保留一个
}
返回一个不超过给定长度的流。如果流是有序的如Stream会返回前n个元素的新流,也可用于无序如Set.
public static void main(String[] args) {
List list = new ArrayList<>();
Collections.addAll(list,"aaa","aaa","Aaaa","b");
List list1=list.stream().limit(3).collect(toList());//只取3个元素,去掉后面的
System.out.println(list1);//[aaa, aaa, Aaaa]
}
跳过给定的n个元素,下如跳过n=2个元素,如果n>=list.size这会返回一个空流
public static void main(String[] args) {
List list = new ArrayList<>();
Collections.addAll(list,"aaa","aaa","Aaaa","b");
List list1=list.stream().skip(2).collect(toList());//跳过前面2个元素,只取后面的
System.out.println(list1);//[Aaaa, b]
}
map会接收一个函数作为参数,如下代码中s.leng()返回Integer,所以map返回的新流是Stream
public static void main(String[] args) {
List list = new ArrayList<>();
Collections.addAll(list,"aaa","aaa","Aaaa","b");
List list1=list.stream().map(s -> s.length()).collect(toList());//配合filter使用
System.out.println(list1);//[3, 3, 4, 1]
}
我们也可以用于处理对象的集合,从中取出一个属性作为集合,相当mysql中从一个表里取出中一列数据
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
//get、set、toString...
}
下面我们取出所有的姓名name,因为user.getName()返回的是String所有map返回的新流是Stream
public static void main(String[] args) {
List list2 = new ArrayList<>();
Collections.addAll(list2,new User("张三",25),new User("李四",26),new User("王五",28));
List list3 = list2.stream().map(user -> user.getName()).collect(toList());
System.out.println(list3);//[张三, 李四, 王五]
}
将流中的每个值都换成另一个流,然后把所有流连接成一个流。
public static void main(String[] args) {
List list4 = new ArrayList<>();
Collections.addAll(list4,"hello","world");
List stream=list4.stream().map(s -> s.split("")).flatMap(strings -> Arrays.stream(strings)).distinct().collect(toList());
System.out.println(stream);//[h, e, l, o, w, r, d]
}
anyMatch就像是:流中至少有一个元素能匹配,anyMatch返回的是一个boolean值,所以不是中间操作而是终端操作
public static void main(String[] args) {
List list2 = new ArrayList<>();
Collections.addAll(list2,new User("张三",25),new User("李四",26),new User("王五",28));
if(list2.stream().anyMatch(user -> user.getName().equals("李四"))){
System.out.println("这个集合中是有一个叫做李四的人");//将会打印出
}
}
allMatch就像是:流中是否所有元素都匹配
public static void main(String[] args) {
List list2 = new ArrayList<>();
Collections.addAll(list2,new User("张三",25),new User("李四",26),new User("王五",28));
if(list2.stream().allMatch(user -> user.getAge()==25)){
System.out.println("这个集合中的人都是25岁");//不会打印,需要所以的年龄都是25,可以自行更改验证
}
}
noneMatch就像是:流中是否所有元素都不匹配。noneMatch和allMatch是相对的。noneMatch是所有都不匹配才是true,而allMatch所有都匹配才是true
public static void main(String[] args) {
List list2 = new ArrayList<>();
Collections.addAll(list2,new User("张三",25),new User("李四",26),new User("王五",28));
if(list2.stream().noneMatch(user -> user.getName().equals("赵六"))){
System.out.println("将会打印输出,因为集合中没有叫做赵六的");//所有都不匹配,打印
}
}
获取任一元素,返回流中任意一个元素,返回的是Optional而不是流,所以也是终端操作。Optional是java8新引入的容器类,代表一个值存在或者不存在,用来处理值为null的问题。(注意findAny返回的只有一个元素,和我们下面将要将的findFirst一样只返回一个)
public static void main(String[] args) {
List list2 = new ArrayList<>();
Collections.addAll(list2,new User("张三",25),new User("李四",26),new User("王五",28));
Optional optional= list2.stream().filter(user -> user.getAge()<=30).findAny();
if (optional.isPresent()){
System.out.println("optional有值我将会打印输出");
}
optional.ifPresent(user -> System.out.println(user.toString()));
User u = optional.get();
User u2 = optional.orElse(new User("赵六",30));
}
Optional操作也很简单,有几个比较常用的方法:
boolean isPresent():将在Optional包含值的时候返回true,否则返回false
void ifPresent(Consumer super T> consumer):会在值存在的时候执行给定的代码块
T get():会在值存在时返回值,否则抛出NoSuchElementException异常
T orElse(T other):值存在时返回值,否则返回默认值other
findFirst与findAny类似也是返回一个元素Optional,findFirst用于有序的流中如Stream等
public static void main(String[] args) {
List list2 = new ArrayList<>();
Collections.addAll(list2,new User("张三",25),new User("李四",26),new User("王五",28));
Optional optional= list2.stream().filter(user -> user.getAge()<=30).findFirst();
}
reduce多用于处理数值,如求最大值、最小值、求和等
如下求和,有六种方法其结果都是一样的。
public static void main(String[] args) {
List list3 = new ArrayList<>();
Collections.addAll(list3,4,7,5,6);
int sum = list3.stream().reduce(0,(a,b)->a+b);
int sum1 = list3.stream().reduce(0,(a,b)->Integer.sum(a,b));
int sum2 = list3.stream().reduce(0,Integer::sum);
Optional optional = list3.stream().reduce((a,b)->a+b);
Optional optional1 = list3.stream().reduce((a,b)->Integer.sum(a,b));
Optional optional2 = list3.stream().reduce(Integer::sum);
System.out.println("总和为:"+sum);//总和为:22
System.out.println("总和为:"+sum1);//总和为:22
System.out.println("总和为:"+sum2);//总和为:22
optional.ifPresent(sum3->System.out.println("总和为:"+sum3));
optional1.ifPresent(sum4->System.out.println("总和为:"+sum4));
optional2.ifPresent(sum5->System.out.println("总和为:"+sum5));
}
理解了上面的六种方法之后,求最大值、最小值方法类似的如:求最大值(记得将reduce方法的第一个参数设置为流中包含的元素)
int max = list3.stream().reduce(4,(a,b)->a>=b?a:b);
int max1 = list3.stream().reduce(4,(a,b)->Integer.max(a,b));
int max2 = list3.stream().reduce(4,Integer::max);
System.out.println("最大值:"+max);
System.out.println("最大值:"+max1);
System.out.println("最大值:"+max2);
当然也可以设置得到的结果是Optional容器类(这个还不需要考虑如何填写第一个参数)
Optional optional3 = list3.stream().reduce((a,b)->a>=b?a:b);
Optional optional4 = list3.stream().reduce((a,b)->Integer.max(a,b));
Optional optional5 = list3.stream().reduce(Integer::max);
sorted处理List
public static void main(String[] args) {
List list = new ArrayList<>();
Collections.addAll(list,"aaa","aaa","Aaaa","b");
List list1=list.stream().sorted(Comparator.comparing(String::length)).collect(toList());
System.out.println(list1);//[b, aaa, aaa, Aaaa]
List list2=list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(toList());
System.out.println(list2);//[Aaaa, aaa, aaa, b]
sorted处理List
public static void main(String[] args) {
List list3 = new ArrayList<>();
Collections.addAll(list3,4,7,5,6);
List integerList = list3.stream().sorted((a,b)->a.compareTo(b)).collect(toList());
System.out.println(integerList);//[4, 5, 6, 7]
List integerList2 = list3.stream().sorted((a,b)->b.compareTo(a)).collect(toList());
System.out.println(integerList2);//[7, 6, 5, 4]
}
sorted处理List
public static void main(String[] args) {
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
List userList=list4.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(toList());//先按年龄从小到大排序,如果年龄相同者根据名字从小到大排序
System.out.println(userList);//[User{name='赵六', age=25}, User{name='张三', age=27}, User{name='李四', age=27}, User{name='王五', age=28}]
List userList2=list4.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(toList());
System.out.println(userList2);//[User{name='王五', age=28}, User{name='张三', age=27}, User{name='李四', age=27}, User{name='赵六', age=25}]
}
计算元素的个数
List list3 = new ArrayList<>();
Collections.addAll(list3,4,7,5,6);
long count = list3.stream().count();//count=4
收集器之前我们也用过也就是collect(toList()),不过我们都是按顺序将流中的元素生成一个列表,收集器的功能可不是仅仅这样,下面将介绍更多。
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
long count = list4.stream().collect(Collectors.counting());//统计
System.out.println(count);//4
long count2 = list4.stream().count();//两种方法结果一样
System.out.println(count);//4
}
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
Optional optional = list4.stream().collect(Collectors.maxBy(Comparator.comparingInt(User::getAge)));//求年龄最大的
optional.ifPresent(System.out::println);//User{name='王五', age=28}
Optional optional2 = list4.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getAge)));//求年龄最小的
optional2.ifPresent(System.out::println);//User{name='赵六', age=25}
}
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
Double ageAverage=list4.stream().collect(Collectors.averagingInt(User::getAge));//求年龄的平均值
System.out.println(ageAverage);//26.75
}
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
Integer sum=list4.stream().collect(Collectors.summingInt(User::getAge));
System.out.println(sum);//107
}
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
IntSummaryStatistics intSummaryStatistics=list4.stream().collect(Collectors.summarizingInt(User::getAge));
System.out.println(intSummaryStatistics);//IntSummaryStatistics{count=4, sum=107, min=25, average=26.750000, max=28}
}
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
String joinName = list4.stream().map(User::getName).collect(Collectors.joining());//只提取名字进行连接
System.out.println(joinName);//张三李四王五赵六
String joinName2 = list4.stream().map(User::getName).collect(Collectors.joining(", "));//只提取名字进行连接,名字之间用", "分开
System.out.println(joinName2);//张三, 李四, 王五, 赵六
String joinName3 = list4.stream().map(User::getName).collect(Collectors.joining(", " , "[" , "]"));//只提取名字进行连接,名字之间用", "分开,并且加上前缀“[”和后缀“]”
System.out.println(joinName3);//[张三, 李四, 王五, 赵六]
String joinStr = list4.stream().map(User::toString).collect(Collectors.joining());//输出全部信息
System.out.println(joinStr);//User{name='张三', age=27}User{name='李四', age=27}User{name='王五', age=28}User{name='赵六', age=25}
}
public static void main(String[] args){
Collections.addAll(list4,new User("张三",27),new User("李四",27),new User("王五",28),new User("赵六",25));
Map> map=list4.stream().collect(Collectors.groupingBy(User::getAge));//根据年龄分组
System.out.println(map);//{25=[User{name='赵六', age=25}], 27=[User{name='张三', age=27}, User{name='李四', age=27}], 28=[User{name='王五', age=28}]}
//上面是根据年龄的某一值分类,现在我们想要按范围分类,age<=25的则分类为"小鲜肉",25> map2 = list4.stream().collect(Collectors.groupingBy(user->{
int age = user.getAge();
if (age<=25){
return "小鲜肉";
}else if (age>25&&age<28){
return "花样年华";
}else {
return "老腊肉";
}
}));
System.out.println(map2);//{老腊肉=[User{name='王五', age=28}], 小鲜肉=[User{name='赵六', age=25}], 花样年华=[User{name='张三', age=27}, User{name='李四', age=27}]}
}
多级分组
为了更好的进行多级分组,我们在User类上加了个sex性别属性。
(注意看:第二个groupingBy是第一个groupingBy方法的参数,所以我们还可以在第二个groupingBy方法上继续加达到更多级的分组)
:先按照年龄再按照性别
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("赵六",25,"男"),new User("钱七",28,"男"));
//先按照年龄范围分组,同一分组中再按照年龄分组
Map>> map2 = list4.stream().collect(Collectors.groupingBy(user->{
int age = user.getAge();
if (age<=25){
return "小鲜肉";
}else if (age>25&&age<28){
return "花样年华";
}else {
return "老腊肉";
}
},Collectors.groupingBy(User::getSex)));
System.out.println(map2);//{老腊肉={男=[User{name='王五', age=28, sex='男'}, User{name='钱七', age=28, sex='男'}]}, 小鲜肉={男=[User{name='赵六', age=25, sex='男'}]}, 花样年华={女=[User{name='李四', age=27, sex='女'}], 男=[User{name='张三', age=27, sex='男'}]}}
}
:先按照性别再按照年龄
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("赵六",25,"男"),new User("钱七",28,"男"));
Map>> map2 = list4.stream().collect(Collectors.groupingBy(User::getSex,Collectors.groupingBy(user->{
int age = user.getAge();
if (age<=25){
return "小鲜肉";
}else if (age>25&&age<28){
return "花样年华";
}else {
return "老腊肉";
}
})));
System.out.println(map2);//{女={花样年华=[User{name='李四', age=27, sex='女'}]}, 男={老腊肉=[User{name='王五', age=28, sex='男'}, User{name='钱七', age=28, sex='男'}], 小鲜肉=[User{name='赵六', age=25, sex='男'}], 花样年华=[User{name='张三', age=27, sex='男'}]}}
}
:我们也可以对子组进行统计或其他操作
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("赵六",25,"男"),new User("钱七",28,"男"));
Map> map=list4.stream().collect(Collectors.groupingBy(User::getSex));//根据性别进行一级分组
System.out.println(map);//{女=[User{name='李四', age=27, sex='女'}], 男=[User{name='张三', age=27, sex='男'}, User{name='王五', age=28, sex='男'}, User{name='赵六', age=25, sex='男'}, User{name='钱七', age=28, sex='男'}]}
Map map2 = list4.stream().collect(Collectors.groupingBy(User::getSex,Collectors.counting()));
System.out.println(map2);//{女=1, 男=4}
}
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("赵六",25,"男"),new User("钱七",28,"男"));
//先按照性别分组,再在子组中选出年龄最大的一个
Map map2 = list4.stream().collect(Collectors.groupingBy(User::getSex,collectingAndThen(maxBy(Comparator.comparingInt(User::getAge)),Optional::get)));
System.out.println(map2);//{女=User{name='李四', age=27, sex='女'}, 男=User{name='王五', age=28, sex='男'}}
}
为了更好的观察分组的功能,先在User类中增加下面一个方法isMan,如果是男的返回true,否则返回false.
public boolean isMan(){
return this.getSex().equals("男")?true:false;
}
partitioningBy分区其实和groupingBy分组几乎一样,区别在于partitioningBy方法第一个参数是一个返回boolean值的函数,得到的结果Map键是boolean,所以 partitioningBy分区只有两种结果,true或者false.
public static void main(String[] args){
List list4 = new ArrayList<>();
Collections.addAll(list4,new User("张三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("赵六",25,"男"),new User("钱七",28,"男"));
Map> map=list4.stream().collect(Collectors.partitioningBy(User::isMan));
System.out.println(map);//{false=[User{name='李四', age=27, sex='女'}], true=[User{name='张三', age=27, sex='男'}, User{name='王五', age=28, sex='男'}, User{name='赵六', age=25, sex='男'}, User{name='钱七', age=28, sex='男'}]}
//分区和分组一样可以对子组进行一些其他操作
Map map2 = list4.stream().collect(Collectors.partitioningBy(User::isMan,counting()));
System.out.println(map2);//{false=1, true=4}
}