Lambda表达式 Stream流

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()进行处理,提高效率,但是并行处理不能保证元素有序,还应注意线程安全问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值