Lambda表达式
1.Lambda表达式使用场景:
Lambda表达式可以取代大部分情况的匿名内部类,注意,正如上面说述,Lambda的应用就是为了取消大部分情况的匿名内部类。(只能替代用于只有一个抽象方法的接口创建匿名内部类的情况,因为需要让程序自己去确定实现的函数以及去创建实例。)[只能是函数式接口,就算是只有一个抽象方法的抽象类也不行!],能够极大的优化代码结构,使代码更加紧凑。
2.Lambda表达式的基本使用:
基本语法:(T …) -> { … },其中(T …)是参数列表,{ … }是方法体, -> 是Lambda运算符,读作 goes to。
Predicate<String> predict = (String val) -> {
if(val.length()>5){
return true;
}else{
return false;
}
};
3.Lambda语法的简化:
(1)简化参数类型,在参数列表中可以省略参数类型(省略必须全部省略)
Predicate<String> predict = (val) -> {
if(val.length()>5){
return true;
}else{
return false;
}
};
(2)当只有一个参数时,可以省略“( )”
Predicate<String> predict = val -> {
if(val.length()>5){
return true;
}else{
return false;
}
};
(3)当描述方法体只有一句话时,可以省略“{ }”
Predicate<String> predict = val ->
return val.length()>5?true:false;
(4)当描述方法体只有一句话且是return语句时,可以省略“return”
Predicate<String> predict = val -> val.length()>5?true:false;
4.Lambda表达式的方法引用:
使用场景:有时候我们不必自己去实现接口中的方法,而是可以直接使用现成的别人写好的方法,这个时候就可以使用Lambda的方法引用。即:使用别人的方法作为函数式接口中未实现的方法,所以对应的要求必须是:参数列表相同,返回值相同,方法名不要求相同。
(1)基本语法:
方法归属者::方法名即:静态方法的方法归属者是类名,实例方法的方法归属者是对象。
Consumer<String> consumer = System.out::println;
(2)构造方法引用:
当函数式接口是返回的某个实例,如果没有特别的功能,就可以直接使用构造方法引用,基本的语法是: 类名::new;
Supplier<Object> supplier = Object::new;
5.Lambda表达式的闭包问题:
匿名内部类在函数中存在的问题,在Lambda中肯定也存在。例如我们在方法中的匿名内部类使用了外部的变量,那么编译器会默认为这个变量加上final修饰,所以下面如果试图去修改这个变量,就会报错:该值是final类型,不能改变。
Stream流
1.Stream的基本介绍:
Java8引入的Stream特性,使得代码结构更加紧凑,简洁易读;且并发模式使得代码执行速度更快。
2.Stream的构成:Source、Intermediate、Terminal。
(1)流的构造(Source)
利用Stream直接构造流:Stream.of(T …)
Stream<String> stream = Stream.of("as", "dsd", "kojj", "fsdd");
利用数组构造流:Arrays.Stream(T[] arr)
String[] strArr = {"as", "dsd", "kojj", "fsdd"};
Stream<String> stream = Arrays.stream(strArr);
集合转换为流:Collection.stream()
String[] strArr = {"as", "dsd", "kojj", "fsdd"};
List<String> list = Arrays.asList(strArr);
Stream<String> stream = list.stream();
文件读取转换为流:java.io.BufferedReader.lines()
List<String> output = reader.lines().
flatMap(line -> Stream.of(line.split(REGEXP))).
filter(word -> word.length() > 0).
collect(Collectors.toList());
自己构建流:Stream.generate(Supplier supplier)
注意:Supplier实现的流是无限的,要么使用short-circuiting操作,要么使用limit限制流。
Random random = new Random();
Stream<Integer> stream = Stream.generate(random::nextInt);
stream.limit(10).forEach(System.out::println);
利用Stream.iterator(T seed, Function fun)构建流,无限流需要short-circuiting操作
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
Stream.iterate(0,val->val+3).limit(10).forEach(System.out::println);
注意:对于基本数值类型有对应的IntStream、LongStream以及DoubleStream,也可以使用泛型限定,但是boxing与Unboxing操作会耗时,应优先使用给定的三种数值流。
(2)转换操作(Intermediate)常用的转换操作主要有:
map (mapToInt, flatMap 等)、 filter、distinct、sorted、peek、limit、 skip、 parallel、sequential、unordered。
示例:
map(Function<T,R> fun) 一对一处理
List<String> output = wordList.stream().
map(String::toUpperCase).
collect(Collectors.toList());
** flatMap(Function<T,Stream> fun) 层级结构扁平化,汇总成流**
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(2, 3),Arrays.asList(4, 5, 6)
);
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
filter(Predict predict) 将predict.test(T t)测试为true的留下形成新stream
List<String> output = reader.lines().
flatMap(line -> Stream.of(line.split(REGEXP))).
filter(word -> word.length() > 0).
collect(Collectors.toList());
(3)终结操作(Terminal)常见的终结操作主要有:
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator。
示例:
forEach(Consumer consumer)遍历操作元素
roster.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.forEach(p -> System.out.println(p.getName()));
注意:由于Stream是Terminal操作,所以不可以在一个Stream中使用两次Terminal操作
stream.forEach(element -> doOneThing(element));
stream.forEach(element -> doAnotherThing(element));
相应的,有peek(Consumer consumer)来进行类似操作,因为peek属于Intermediate操作,不会终止流。
Stream.of("one", "two", "three", "four")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
这里应该注意的是,程序的输出结果是:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
所以,从这里可以看到Stream对元素的执行顺序是每一个元素依次执行,而不是每一阶段的流向下整体执行
Option findFirst()返回流中第一个元素,属于Terminal兼short-circuiting操作
public static int getNum(){
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
//stream3.findFirst().ifPresent(System.out::println);
return stream3.limit(0).findFirst().orElse(-1);
}
这里需要注意的是,对于此类操作,返回的是Optional包装类型,有对应的处理方法如orElse(T t)、ifPresent(Consumer consumer)等。在程序逻辑中可以在一定程度上防止NPE问题。类似的操作还有:findAny、max/min、reduce等。
T reduce(T identity, BinaryOperator accumulator) 将元素进行迭代结合,其实max/min、sum、average都属于封装的reduce操作
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
//注意加不加identity初始值的区别,很合理
Optional<Integer> reduce = stream3.limit(10).reduce(Integer::sum);
Integer sum = stream3.limit(10).reduce(0, Integer::sum);
limit/skip(Long n) 限定流条件
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
int num = stream3.limit(10).skip(3).collect(Collectors.toList()).size();
System.out.println(num);
sorted(Comparator<? super T> comparator)对元素进行排序
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
stream3.limit(10).sorted((o1, o2) -> o2-o1).forEach(System.out::println);
min/max/distinct()
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
stream3.limit(10).distinct().max((o1, o2) -> o1-o2).ifPresent(System.out::println);
br.lines().flatMap(line -> Stream.of(line.split(" ")))
.filter(word -> word.length() > 0)
.map(String::toLowerCase)
.distinct()
.sorted()
.collect(Collectors.toList());
br.close();
System.out.println(words);
(4)短路操作(short-ciecuiting)
常见的短路操作有:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit示例:
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
boolean b = stream3.limit(10).noneMatch(val -> val > 0);
System.out.println(b);
(5)利用Collectors进行分组操作(groupingBy/partitioningBy)
java.util.stream.Collectors 类的主要作用就是辅助进行各类有用的 reduction 操作,例如转变输出为 Collection,把 Stream 元素进行归组。
Collectors.groupingBy(Function<? super T, ? extends K> classifier)按照Function条件进行分组
Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
limit(100).
collect(Collectors.groupingBy(Person::getAge));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, List<Person>> persons = (Map.Entry) it.next();
System.out.println("Age " + persons.getKey() + " = " + persons.getValue().size());
}
Collectors.partitioningBy(Predicate<? super T> predicate) 按照predict条件分为两组
Random random = new Random();
Stream<Integer> stream3 = Stream.generate(random::nextInt);
Map<Boolean, List<Integer>> collect = stream3.limit(10).collect(Collectors.partitioningBy(val -> val > 0));
Set<Boolean> keySet = collect.keySet();
keySet.forEach(item->collect.get(item).forEach(System.out::println));
以上,多线程对应的处理方法如parallelStream()进行处理,提高效率,但是并行处理不能保证元素有序,还应注意线程安全问题。