Lambda为java8带来了闭包,支持对集合对象的stream进行函数式操作, stream api被集成进了collection api ,允许对集合对象进行批量操作。
Stream表示数据流,它没有数据结构,本身也不存储元素,其操作也不会改变源Stream,而是生成新Stream.作为一种操作数据的接口,它提供了过滤、排序、映射、规约等多种操作方法,
这些方法按照返回类型被分为两类:凡是返回Stream类型的方法,称之为中间方法(中间操作),其余的都是完结方法(完结操作)。完结方法返回一个某种类型的值,而中间方法则返回新的Stream。
中间方法的调用通常是链式的,该过程会形成一个管道,当完结方法被调用时会导致立即从管道中消费值,这里我们要记住:Stream的操作尽可能以“延迟”的方式运行,也就是我们常说的“懒操作”,
这样有助于减少资源占用,提高性能。对于所有的中间操作(除sorted外)都是运行在延迟模式下。
Stream不但提供了强大的数据操作能力,更重要的是Stream既支持串行也支持并行,并行使得Stream在多核处理器上有着更好的性能。
Stream的使用过程有着固定的模式:
1.创建Stream
2.通过中间操作,对原始Stream进行“变化”并生成新的Stream
3.使用完结操作,生成最终结果
//创建一个集合
List<String> list = new ArrayList<>();
list.add("a1");list.add("a2");list.add("a3");list.add("b1");list.add("b2");list.add("b3");
结合Predicate接口,Filter对流对象中的所有元素进行过滤,该操作是一个中间操作,这意味着你可以在操作返回结果的基础上进行其他操作
public static void sreamFilterTest(List<String> lists){ //要明确这list的泛型类型,否则jvm不能根据上下文确定参数类型
lists.stream()
.filter((s -> s.startsWith("a")))
.forEach(System.out::println);//将开头是a的过滤出来
//等价于以上操作
Predicate<String> predicate = (s) -> s.startsWith("a");//将开头是a的过滤出来
lists.stream()
.filter(predicate)
.forEach(System.out::println);
//连续过滤
Predicate<String> predicate1 = (s -> s.endsWith("1"));//将开头是a,并且结尾是1的过滤出来
lists.stream()
.filter(predicate
.filter(predicate1)
.forEach(System.out::println);
}
结合Comparator,该操作返回一个排序过后的流的视图,原始流的顺序不会改变。通过Comparator来指定排序规则,默认是自然排序
sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。
//自然排序
List<String> collect = list.stream().sorted().collect(Collectors.toList());
list.stream().sorted(Comparator.comparing(Student::getAge).reversed())
//定制排序
List<String> collect2 = list.stream().sorted((o1, o2) -> {
if(o1.length()>o2.length()){
return 1;
}else
if(o1.length()<o2.length()){
return -1;
}else {
return 0
}
}).collect(Collectors.toList());
结合Function接口,该操作能将流对象中的每一个元素映射为另一个元素,实现元素类型的转换。
private static void streamMapTest(List<String> list){
list.stream()
.map(String::toUpperCase)
.sorted((s, t1) -> t1.compareTo(s))
.forEach(System.out::println);
System.out.println("- - - - - - ");
//自定义映射规则
Function<String,String> function = s -> {return s + ".map3";};
list.stream().map(function).forEach(System.out::println);
}
用来判断某个predicate是否和流对象相匹配,最终返回boolean类型的结果
private static void streamMatchTest(List<String> list){
//流对象中只要有一个元素匹配就返回true
boolean anyStartWithA = list.stream().anyMatch(s -> s.startsWith("a"));
System.out.println("集合中是否有以'a'来头:"+ anyStartWithA);
//流对象中每一个元素都匹配才返回true
boolean allStartWithA = list.stream().allMatch(s -> s.startsWith("a"));
System.out.println("集合中每一个都是以'a'开头:"+ allStartWithA);
//流对象中没有匹配时返回true
boolean noneStartWithA = list.stream().noneMatch(s -> s.startsWith("c"));
System.out.println("集合中没有以'c'开头:"+ noneStartWithA);
}
在对经过变换后,将变换的stream元素收集,比如将这些元素存在集合中,可以使用stream提供的collect方法
private static void streamCollectTest(List<String> list){
List<String> listNew = list.stream()
.filter(s -> s.startsWith("b"))
.sorted()
.collect(Collectors.toList());
System.out.println(listNew );
}
用来统计流中元素的总数
private static void streamCountTest(List<String> list){
long count = list.stream().filter(s -> s.startsWith("b")).count();
System.out.println("以'b'开头的数量:"+ count);
}
可以配合其他得中间操作,并截断流,取到相应的元素个数,这不会往下执行,可以提高效率
List<String> list = Arrays.asList("1","2","3","4","0","5","6","7","8");
Stream<String> stream = list.stream().filter((x) -> {
System.out.println(" api 中建操作。");
return x.equals("3");
});
//取两个 , 可以配合其他得中间操作,并截断流,取到相应的元素个数,这不会往下执行,可以提高效率
stream.limit(2).forEach(System.out::println);
skip(n),返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空,与limit(n)互补。
List list = Arrays.asList("1","2","3","4","0","5","6","7","8");
Stream stream = list.stream().filter((x) -> {
System.out.println(" api 中建操作。");
return x.equals("3");
});
//skip(n),返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空,与limit(n)互补。
stream.skip(3).limit(1).forEach(System.out::println);
distinct 通过流所生成元素的hashCode()和equals()去除重复元素
List list = Arrays.asList("1","2","3","4","0","5","6","7","8");
Stream stream = list.stream();
stream.distinct().forEach(System.out::println);
示例实体类:
public class Person {
String name ;
String sex ;
int age;
Status statusEnum;
public Person(String name, String sex, int age, Status statusEnum) {
this.name = name;
this.sex = sex;
this.age = age;
this.statusEnum = statusEnum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Status getStatusEnum() {
return statusEnum;
}
public void setStatusEnum(Status statusEnum) {
this.statusEnum = statusEnum;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", statusEnum=" + statusEnum +
'}';
}
}
示例操作:
List persons = Arrays.asList(
new Person("小红", "男", 76, Status.FREE),
new Person("小明", "女", 12, Status.BUSY),
new Person("小白", "男", 35, Status.BUSY),
new Person("小李", "男", 3, Status.FREE),
new Person("小花", "男", 56, Status.BUSY),
new Person("如花", "女", 34, Status.VOCATION),
new Person("似玉", "女", 34, Status.FREE),
new Person("芙蓉", "女", 34, Status.VOCATION)
);
public void test1(){
boolean allMatch = persons.stream().allMatch((x) -> {
return x.getStatusEnum().equals(Status.FREE);
});
System.out.println(allMatch);
}
public void test2(){
boolean allMatch = persons.stream().anyMatch((x) -> {
return x.getStatusEnum().equals(Status.FREE);
});
System.out.println(allMatch);
}
// 检查 所有的是否都是 FREE ----- 结果是false
public void test3(){
boolean allMatch = persons.stream().noneMatch((x) -> {
return x.getStatusEnum().equals(Status.FREE);
});
System.out.println(allMatch);
}
public void test4(){
Optional<Person> first = persons.stream().findFirst();java
System.out.println(first.get());
}
public void test5(){
Optional first = persons.stream().findAny();
//first.orElse(new Person()); 如果没空 可以创一个新的对象去代替它
System.out.println(first.get());
}
public void test6(){
long first = persons.stream().count();
System.out.println(first);
}
public void test7(){
Optional person = persons.stream().max((x,y) -> Integer.compare(x.age, y.age));
System.out.println(person.get());
}
public void test8(){
Optional person = persons.stream().min((x,y) -> Integer.compare(x.age, y.age));
System.out.println(person.get());
}
可以将流中元素反复结合在一起,得到一个值
reduce(T identitty,BinaryOperator)首先,需要传一个起始值,然后,传入的是一个二元运算。
public void test9(){
List list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// identitty 起始值 0 ,然后与集合中的值进行 相应的运算,再次赋值给 identity 然后 在进行运算。
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
}
reduce(BinaryOperator)此方法相对于上面方法来说,没有起始值,则有可能结果为空,所以返回的值会被封装到Optional中。
//map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。
//用map 来提取 对象中某个属性,然后再用reduce 进行归约。
public void test10() {
Optional<Integer> reduce = persons.stream().map(Person::getAge).reduce(Integer::sum);
System.out.println(reduce.get());
}
——————————————————补充————————————
这里的reduce.get()
用reduce.orElse()
替代会更好 get只是获取他实例的值 如果没有就会抛出异常,orElse()传入一个参数 ,会用括号中的值替代为null的值
收集 : 收集collect(将流转换为其他形式。接收一个Collector接口得实现,用于给其他Stream中元素做汇总的方法)
Collector接口中方法得实现决定了如何对流执行收集操作(如收集到List,Set,Map)。
但是Collectors实用类提供了很多静态方法,可以方便地创建常见得收集器实例。
public void test11() {
List<Person> collect = persons.stream().collect(Collectors.toList());
collect.forEach(System.out::println);
}
public void test12() {
//hashset会去重复
Set collect = persons.stream().map(Person::getName).collect(Collectors.toSet());
collect.forEach(System.out::println);
}
public void test13() {
Set<Integer> collect = persons.stream().map(Person::getAge).collect(Collectors.toCollection(LjavainkedHashSet::new));
collect.forEach(System.out::println);
}
public void test14() {
Long collect = persons.stream().map(Person::getAge).collect(Collectors.counting());
System.out.println(collect);java
}
将流转换为其他形式 , 接受一个conllectors接口的实现,用于给Stream中元素
public void test14s() {
// 1 对元素进行汇总方法
DoubleSummaryStatistics collect = persons.stream().collect(Collectors.summarizingDouble(Person::getAge));
IntSummaryStatistics collect2 = persons.stream().collect(Collectors.summarizingInt(Person::getAge));
System.out.println(collect.getMax());
System.out.println(collect.getAverage());
System.out.println(collect.getCount());
System.out.println(collect.getMin());
System.out.println(collect.getSum());
String collect1 = persons.stream().map(Person::getName).collect(Collectors.joining(",","头","尾"));
String collect3 = persons.stream().map(Person::getName).collect(Collectors.joining());
System.out.println(collect1); //头小红,小明,小白,小李,小花,如花,似玉,芙蓉尾
System.out.println(collect3); // 小红小明小白小李小花如花似玉芙蓉
}
1. Collectors.averagingDouble()
2 Collectors.averagingDouble()
3 Collectors.averagingLong()
平均数,这三个方法都可以求平均数,不同之处在于传入得参数类型不同,返回值都为Double
public void test15() {
Double s = persons.stream().collect(Collectors.averagingDouble(Person::getAge));
System.out.println(s);
}
public void test16() {
Optional collect = persons.stream().collect(Collectors.maxBy((o1, o2) -> Integer.compare(o1.age, o2.age)));
System.out.println(collect.get().age);
}
public void test17() {
Optional collect = persons.stream().collect(Collectors.minBy((o1, o2) -> Integer.compare(o1.age, o2.age)));
System.out.println(collect.get().age);
}
//按照 Status 分组
public void test18() {
Map> collect = persons.stream().collect(Collectors.groupingBy(Person::getStatusEnum));
collect.forEach((status, people) -> {
System.out.println(" status === " + status);
people.forEach(System.out::println);
});
}
Collectors.groupingBy()还可以实现多级分组
public void test19() {
Map>> collect = persons.stream().collect(Collectors.groupingBy(Person::getStatusEnum,Collectors.groupingBy(Person::getSex)));
Map>> collect2 = persons.stream().collect(Collectors.groupingBy(Person::getStatusEnum,Collectors.groupingBy(
e->{
if (e.getAge()>10){
return "小孩";
}else {
return "大人";
}
}
)));
System.out.println(collect);
System.out.println(collect2);
}
//Collectors.partitioningBy() 分区,参数中传一个函数,返回true,和false 分成两个区
public void test20() {
//年龄大于39的分区 不满足再的分区
Map<Boolean, List<Person>> collect = persons.stream().collect(Collectors.groupingBy(e -> e.getAge() > 30));
System.out.println(collect);
}
并行Stream:基于Fork-join并行分解框架实现,将大数据集合切分为多个小数据结合交给不同的线程去处理,这样在多核处理情况下,性能会得到很大的提高。
这和MapReduce的设计理念一致:大任务化小,小任务再分配到不同的机器执行。只不过这里的小任务是交给不同的处理器。
结果是性能提高50%,单核下还是串行流性能比较好,并行流的使用场景是多核+大数据
//创建一个大集合
List list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
UUID uuid = UUID.randomUUID();
list.add(uuid.toString());
}
//并行stream
private static void parallelStreamSortedTest(List list){
long startTime = System.nanoTime();//返回最准确的可用系统计时器的当前值,以毫微秒为单位。
long count = list.parallelStream().sorted().count();
long endTime = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.printf("并行排序花费时间:%d ms",millis);
}
//串行stream
private static void streamSortedTest(List list){
long startTime = System.nanoTime();//返回最准确的可用系统计时器的当前值,以毫微秒为单位。
long count = list.stream().sorted().count();
long endTime = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.printf("串行排序花费时间:%d ms",millis);
}