文章目录
注:任何流操作都要有 终结操作
forEach就是一个终结操作,如果没有它,那么我们写的filter ,distinct 是没有用的
authors.stream()//把集合转换成流
.distinct()
.filter(author -> {
System.out.println("test");
return author.getAge() < 18;
})
.forEach(author -> System.out.println(author.getName()));
如果没有forEach 终结操作,那么filter中操作时不会执行的,就不会打印 “test”
常用操作
创建流
单列集合:集合对象.stream()
List<Author> authors;
Stream<Author> stream = authors.stream();
数组:Arrays.stream(arr) 或 Stream.of(arr);
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream = Stream.of(arr);
stream.distinct() //去重
.filter(integer -> integer>2) //数组里大于2的元素才会留下
.forEach(integer -> System.out.println(integer));
双列集合:转换成单列集合后再创建
Map<String,Integer> map = new HashMap<>();
map.put("蜡笔小新",19);
map.put("黑子",17);
map.put("日向翔阳",16);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Stream<Map.Entry<String, Integer>> stream = entrySet.stream();
stream.filter(entry -> entry.getValue()>16)
.forEach(entry -> System.out.println(entry.getKey()+"==="+entry.getValue()));
中间操作
filter:
对流中的元素进行筛选
一定要有终结操作forEach,否则filter不会执行
List<Author> authors = getAuthors();
// 打印所有姓名长度大于1的作家的姓名
authors.stream()
.filter(author -> author.getName().length()>1)
.forEach(author -> System.out.println(author.getName()));
//一定要有终结操作
map
可以把对流中的元素进行计算或转换(转换流中的类型)
// 打印所有作家的姓名
List<Author> authors = getAuthors();
============其实可以不用map=================
authors.stream()
.forEach(s -> System.out.println(s.getName()));
--------------------------用流操作------------------------------------------
authors.stream()
.map(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getName();
}
})
.forEach(s -> System.out.println(s));
通过map把age+10,然后只返回age,只打印age
distinct
可以去除流中的重复元素
注意:distinct 方法是依赖Object的equals 方法来判断是否是相同对象的。所以需要重写equals 方法
字符串已经自己重写了equals方法,会判断字段是否相同,而不是判断地址
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.forEach(author -> System.out.println(author.getName()));
自己定义的类需要考虑是否重写equals 方法
@Data相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集。
注解:@EqualsAndHashCode//用于后期的去重使用
sorted
可以对流中的元素进行排序
例如:对流中的元素按照年龄进行降序排列,并且要求不能有重复的元素
sorted空参:对象类一定要实现Compareable接口,重写compareTo方法 ,会把流中的对象转化为 Copareable ,使用对象类要实现 Compareable接口。
sorted如果有参数,对象类可以不实现compareTo方法,因为sorted方法里面有Comparator参数
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Author implements Comparable<Author>{
//id
private Long id;
//姓名
private String name;
//年龄
private Integer age;
//简介
private String intro;
//作品
private List<Book> books;
@Override
public int compareTo(Author o) {
return o.getAge()-this.getAge();
}
}
authors.stream()
.distinct()
.sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o2.getAge() - o1.getAge();
}
})
.forEach(author -> System.out.println(author.getAge()));
====================lambda====================
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.forEach(author -> System.out.println(author.getAge()));
authors.stream()
.distinct()
.sorted()
.forEach(author -> System.out.println(author.getAge()));
limit
可以设置流的最大长度,超出的部分将被抛弃
例子:对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家的名字
authors.stream()
.distinct()
.sorted()
.limit(2)
.forEach(author -> System.out.println(author.getName()));
skip
跳过流中的前n个元素,返回剩下的元素
打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted()
.skip(1)
.forEach(author -> System.out.println(author.getName()));
flatMap
map只能把一个对象转换成另一个对象来作为流中的元素,而 flatmap 可以把一个对象转换成多个对象作为流中的对象
map:一个对象 —> 另一个对象
flatMap:一个对象 —> 多个对象
例:
打印所有书籍的名字。要求对重复的元素进行去重。
你可能是这样写的,但是这是错误的。因为map返回的是一个书的名字的集合,这打印出来的是一个个集合,然后集合里才是书籍,而我们想要的直接就是书籍。并且distinct去重的是集合,也不是我们像要的书籍去重
authors.stream()
.map(author -> author.getBooks())
.distinct()
.forEach(s -> System.out.println(s));
正确的写法:
authors.stream()
.flatMap(new Function<Author, Stream<? extends Book>>() {
@Override
public Stream<? extends Book> apply(Author author) {
return author.getBooks().stream();
}
})
.distinct()
.forEach(book -> System.out.println(book.getName()));
优化:
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.forEach(book -> System.out.println(book.getName()));
把一个集合(对象)转化成多个对象:
再次练习:
打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式:哲学,爱情
authors.stream()
.flatMap(new Function<Author, Stream<? extends Book>>() {
@Override
public Stream<? extends Book> apply(Author author) {
return author.getBooks().stream();
}
})
.distinct()
.flatMap(new Function<Book, Stream<?>>() {
@Override
public Stream<?> apply(Book book) {
return Arrays.stream(book.getCategory().split(","));
}
})
.distinct()
.forEach(s-> System.out.println(s));
第一个flatMap是把 书籍的集合 转化成 书籍流,第二个flatMap是把书籍(一本书可以有多个类型)流转化成 类型流。
优化:
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.distinct()
.forEach(category-> System.out.println(category));
终结操作
终结操作:forEach
forEach,如果没有它,那么我们写的filter ,distinct 是没有用的
authors.stream()//把集合转换成流
.distinct()
.filter(author -> {
System.out.println("test");
return author.getAge() < 18;
})
.forEach(author -> System.out.println(author.getName()));
如果没有forEach 终结操作,那么filter中操作时不会执行的,就不会打印 “test”
没有终结操作 stream流式不会执行的
forEach
对流中的元素进行遍历查找,我们通过传入的参数去指定对遍历到的元素进行说明具体操作。
authors.stream()
.map(author -> author.getName())
.distinct()
.forEach(name-> System.out.println(name));
count
可以用来获取当前流中元素的个数
打印这些作家的所出书籍的数目,注意删除重复元素。
long count1 = authors.stream()
.flatMap(new Function<Author, Stream<?>>() {
@Override
public Stream<?> apply(Author author) {
return author.getBooks().stream();
}
})
.distinct()
.count();
System.out.println(count1);
max & min
可以用来获取流中的最值
分别获取这些作家的所出书籍的最高分和最低分并打印。
//Stream<Author> -> Stream<Book> ->Stream<Integer> ->求值
List<Author> authors = getAuthors();
Optional<Integer> max1 = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((a, b) -> a - b);
Optional<Integer> min1 = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.min(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return a - b;
}
});
System.out.println(max1.get());
System.out.println(min1.get());
终结操作
collect
把当前流转换成一个集合
获取一个存放所有作者名字的List集合。
List<Author> authors = getAuthors();
List<String> nameList = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(nameList);
Collectors 是一个工具类
再练习:
获取一个所有书名的Set集合。
List<Author> authors = getAuthors();
Set<Book> collect = authors.stream()
.flatMap(author -> author.getBooks().stream())
.collect(Collectors.toSet());
System.out.println(collect);
再次练习:
把作者名和他所写的数据 以map键值对的形式放在map中,用toMap方法,Collectors.toMap 里面有两个参数,一个是用来获取key,另一个用来获取value
需要先去重,去重复的key
获取一个Map集合,map的key为作者名,value为List<Book>
Map<String, List<Book>> collect = authors.stream()
.distinct()
.collect(Collectors.toMap(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getName();
}
}, new Function<Author, List<Book>>() {
@Override
public List<Book> apply(Author author) {
return author.getBooks();
}
}));
System.out.println(collect);
查找与匹配
anyMatch
anyMatch 可以用来判断是否有任意匹配条件的元素,结果为boolean 类型
如果有一个符合条件就返回true
判断是否有年龄在29以上的作家
List<Author> authors = getAuthors();
boolean flag = authors.stream()
.anyMatch(author -> author.getAge() > 29);
System.out.println(flag);
allMatch
可以用来判断是否都符合匹配条件,只有所有数据都符合条件才返回true。
判断是否所有的作家都是成年人
List<Author> authors = getAuthors();
boolean flag = authors.stream()
.allMatch(author -> author.getAge() >= 18);
noneMatch
可以判断流中的元素是否都不符合匹配条件,如果都不符合结果 返回true。
判断作家是否都没有 超过100岁的。
List<Author> authors = getAuthors();
boolean b1 = authors.stream()
.noneMatch(author -> author.getAge() > 100);
System.out.println(b1);
打印 true
注:里面写 超过100岁的,都没有超过100才返回true,(有一点绕)
findAny
获取流中的任意一个元素,该方法没有办法保证获取的一定是流中的第一个元素。
获取任意一个年龄大于18的作家,如果存在就输出他的名字
List<Author> authors = getAuthors();
Optional<Author> optionalAuthor = authors.stream()
.filter(author -> author.getAge()>18)
.findAny();
optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
findFirst
获取流中的第一个元素
获取一个年龄最小的作家,并输出他的姓名。
List<Author> authors = getAuthors();
Optional<Author> first1 = authors.stream()
.sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o1.getAge() - o2.getAge();
}
})
.findFirst();
first1.ifPresent(author -> System.out.println(author.getName()));
升序排序,然后获取第一个元素,就是最小的。
reduce归并
对流中的数据按照你制定的计算方式计算出一个结果
reduce 的作用是把stream 中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和在初始化值的基础上进行计算,计算结果再和后面的元素计算
T result = identity
for(T element : this stream){
result = accumulator.apply(result,element)
}
return result;
其中identity 就是我们可以通过方法参数传入的初始值,accumulate 的apply 具体进行什么计算也是我们通过 方法参数来确定的。
例1:
使用reduce求所有作者年龄的和
List<Author> authors = getAuthors();
Integer sum = authors.stream()
.distinct()
.map(author -> author.getAge())
.reduce(0, (result, element) -> result + element);
System.out.println(sum);
例2:
使用reduce求所有作者中年龄的最大值
List<Author> authors = getAuthors();
Integer max = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
System.out.println(max);
例3:
使用reduce求所有作者中年龄的最小值
List<Author> authors = getAuthors();
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MAX_VALUE, (a, b) -> a < b ? a : b);
System.out.println(reduce);
例4:一个参数
使用reduce求所有作者中年龄的最小值
List<Author> authors = getAuthors();
Optional<Integer> minOptional = authors.stream()
.map(author -> author.getAge())
.reduce((result, element) -> result > element ? element : result);
minOptional.ifPresent(System.out::println);
一个参数的会把第一个元素作为初始化值,和上面传 初始值为0 的没有区别
注意事项
- 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
- 流是一次性的(一旦一个流对象经过一个终结操作后,这个流就不能再被使用)
- 不会印象原数据,像map这种映射是不影响原来集合的。
Optional
让代码更加安全,防止空指针异常
创建对象
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
authorOptional.ifPresent(author1 -> System.out.println(author1.getName()));
通过ofNullable获取的Optional,如果里面的参数是null 也不会包空指针异常
通过of() 获取的Optional,如果of里面的参数是null的话,会出现异常。
这样写有一点麻烦:改进一下
我们可以让返回值 直接返回Optional对象
Optional<Author> authorOptional = getAuthorOptional();
authorOptional.ifPresent(author1 -> System.out.println(author1.getName()));
Mybatis 从3.5版本也支持Optional了,我们可以直接把到方法的返回值类型定义成 Optional类型,Mybatis 会自己把数据封装成Optional返回,不需要我们自己操作
安全消费值
获取Optional中的数据
可以直接 使用get()方法。(但如果Optional为null,就会异常)
Optional<Author> authorOptional = Optional.ofNullable(author);
Author author2 = authorOptional.get();
也可以使用:orElseGet方法
设置如果Optional 如果为null,返回什么。
Optional<Author> authorOptional = getAuthorOptional();
Author author = authorOptional.orElseGet(() -> new Author(2L,"三更",15,"一个从菜刀中明悟哲理的祖安人",null));
System.out.println(author.getName());
orElseThrow:如果为null,抛出一个异常
Optional<Author> authorOptional = getAuthorOptional();
try {
Author author = authorOptional.orElseThrow(() -> new RuntimeException("数据为null"));
System.out.println(author);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
函数式接口
只有一个抽象方法的接口我们称为函数式接口
jdk的函数式接口 都加上了@FunctionalInterface 注解。
public interface Consumer<T> {
void accept(T t);
方法引用
基本格式:类名或者对象名::方法名
第一个是lambda表达式,第二个是方法引用
lambda表达式:
authors.parallelStream()
.map(author -> author.getAge())
方法引用:
authors.parallelStream()
.map(Author::getAge)
引用类的静态方法
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。
引用类的静态方法:
authors.stream()
.map(author -> author.getAge())
.map(String::valueOf);
完整写法:
authors.stream()
.map(author -> author.getAge())
.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return String.valueOf(integer);
}
});
这也是一个调用静态方法的方法引用
authors.stream()
.forEach(System.out::println);
引用对象的实体方法
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法
StringBuilder s = new StringBuilder();
引用对象的实体方法:
authors.stream()
.map(Author::getName)
.forEach(s::append);
完整写法:
authors.stream()
.map(Author::getName)
.forEach(new Consumer<String>() {
@Override
public void accept(String name) {
s.append(name);
}
});
引用类的实例方法
上一个是 引用对象的实体方法,这个是 引用类的实例方法
如果我们中重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法
引用类的实例方法:
subAuthorName("三更草堂", String::substring);
完整写法:
subAuthorName("三更草堂", new UseString() {
@Override
public String use(String s, int beginIndex, int endIndex) {
return s.substring(beginIndex, endIndex);
}
});
构造器引用
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器
构造器引用:
authors.stream()
.map(Author::getName)
.map(StringBuilder::new)
.map(sb->sb.append("-三更").toString())
.forEach(System.out::println);
完整写法:
authors.stream()
.map(Author::getName)
.map(new Function<String, StringBuilder>() {
@Override
public StringBuilder apply(String str) {
return new StringBuilder(str);
}
})
.map(sb->sb.append("-三更").toString())
.forEach(System.out::println);
lambda表达式:
authors.stream()
.map(Author::getName)
.map(str -> new StringBuilder(str))
.map(sb->sb.append("-三更").toString())
.forEach(System.out::println);
关于自动装箱和封箱的优化
频繁的int 和 Integer 的转化可能会导致性能下降,所以我们使用mapToInt ,这样后面的map和filter 调用的时候就直接拿到 int 而不是Integer了。
authors.stream()
.mapToInt(value -> value.getAge())
.map(age->age+10)
.filter(age->age>18);
并行流
当流中有大量元素时,我们可以使用并行流去提高操作的效率,其实并行流就是把任务分配给多线程去完成。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识,而如果我们使用stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率
.parallel():使用多线程可以把串行流转换成并行流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = stream.parallel()
.peek(new Consumer<Integer>() {
@Override
public void accept(Integer num) {
System.out.println(num+Thread.currentThread().getName());
}
})
.filter(num -> num > 5)
.reduce((result, ele) -> result + ele)
.get();
System.out.println(sum);