Stream允许我们以声明式的方式处理数据集合,可以将Stream看作是遍历数据集合的高级迭代器。Stream 与Lambda表达式结合使用,将使编码效率大大提高,可读性增强。
注意:Stream与IO中的InputStream/OutputStream是不同的概念
操作案例:使用stream将集合中的手机按照序号num从小到大排序:
结果:
Stream流的操作顺序:
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Integer[] arr= new Integer[10];
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream = Stream.of(4,3,2,7,5,1,6);
BufferedReader reader=new BufferedReader(new FileReader("test.txt"));
Stream<String> lineStream = reader.lines();
流操作可以分为中间操作和终端操作
List<Apple> apples =
applestore .stream() 获得流
.filter(a -> a.getColor().equals("红色")) 中间操作
.collect(Collectors.toList()); 终端操作
简单来说Stream的流程就是
数据源->中间操作->终端操作->结果
/**
* 终端操作 -- 结果
*/
Integer[] arr = new Integer[]{3,1,5,7,2,6,4,4,0};
Object[] arr1 = Arrays.stream(arr)
.distinct()
.sorted()
.toArray(); //toArray()将流中元素倒入Object数组中
//forEach()
Arrays.stream(arr1).forEach(e-> System.out.print(e+" "));
//count 统计
Long count = Arrays.stream(arr).count();
System.out.println(count);
//anyMatch 流中任意一个元素符合条件时返回true
boolean any = Arrays.stream(arr).anyMatch((e)->{return e>3;});
System.out.println("有1个元素>3 :"+any);
//anyMatch 流中所有元素符合条件时返回true
boolean all = Arrays.stream(arr).allMatch((e)->{return e>3;});
System.out.println("所有元素>3 :"+all);
//findFirst 返回流中第一个元素
System.out.println("流中第一个元素 :"+Arrays.stream(arr).findFirst());
//reduce:所有元素求和
Optional<Integer> res = Arrays.stream(arr).reduce((a, b)->a+b);
System.out.println("元素求和 :"+res);
根据对象ID属性将java集合拆分为子集合
Map<Integer, List<LotRealTimeProductionInfo>> retByIdMap =
lotRetInfoList.stream().collect(Collectors.groupingBy(LotRealTimeProductionInfo::getLotId));
Lambda表达式是Java8增加的语言级的新特性。
在Java中,一切皆对象, 数组、类的实例都是对象。
在Java中定义函数或者方法不可能完全独立,也不能将方法作为参数或返回一个方法给实例。
在Java8之前,要实现将某些功能传递给某个方法,要写匿名内部类:
String[] arr = {"c","a","d","b"};
//Arrays.sort(arr,new StringSort());// 外部类
//Arrays.sort(arr,new InnerStringSort());// 内部类
//new + 接口/抽象类 创建匿名内部类对象
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
System.out.println(Arrays.toString(arr));
为了对集合进行排序,为Comparator接口创建了一个匿名内部类对象,重写了接口中的方法来实现排序的功能。
Lambda表达式是一个匿名函数,可以将Lambda表达式理解为一段可以传递的代码(将代码段数据一样传递)。使用Lambda表达式可以写出更简洁,更灵活的代码。
Lambda表达式的本质只是一个语法糖,有编译器推断并帮助我们转换包装为常规的代码,因此我们可以使用更少的代码来实现相同的功能。
Java中的Lambda表达式通常使用(argument)->{body}
的语法书写:
->的左侧:Lambda表达式的参数列表
->的右侧:Lambda表达式中需要执行的功能,及Lambda体
(参数1,参数2,...)->{函数体}
(类 类实例,类 类实例,...)->{函数体}
下面是一些Lambda表达式的例子:
() -> System.out.println("Hello World");
() -> { return 3.1415 };
(String s) -> { System.out.println(s); }
s -> { System.out.println(s); }
(int a, int b) -> { return a + b; }
(a, b) -> { return a - b; }
(int a,int b)与 (a,b)相同
。()->100
。(a,b)->return a+b;
x->return x*x;
。package com.ffyc.forword.lambd;
import java.util.Arrays;
public class LambdDemo {
/*
lambda 语法
(参数列表)->{函数体}
是匿名函数,一般在方法的参数为"接口"时,代替匿名内部类使用(Java8新特性)
(a,b) 参数类型可以不写,可以根据上下文自动推断
推断依据?
要求接口中只有一个抽象方法
@FunctionalInterface 功能接口(java8)单抽象方法接口
public interface Comparator
*/
public static void main(String[] args) {
String[] arr = {"c","a","d","b"};
//用lambda表达式代替匿名内部类对象 将接口中的函数作为参数传递
Arrays.sort(arr,(o1,o2)->{ return o2.compareTo(o1);});
System.out.println(Arrays.toString(arr));
}
}
//匿名内部类方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run...");
}
}).start();
//Lambda表达式
new Thread( ()->{System.out.println("run...");} ).start();
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
Arrays.sort(arr,(o1,o2)->{ return o1.compareTo(o2);});
List<Integer> list = Arrays.asList(4,1,3,2);
System.out.println(list);//[4, 1, 3, 2]
list.sort((o1,o2)->{return o1.compareTo(o2);});
System.out.println(list);//[1, 2, 3, 4]
// 匿名内部类方式
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hello world");
}
});
// lambda 表达式方式
button.addActionListener( (e) -> { System.out.println("Hello world"); });
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//foreach遍历
for (Integer i : list) {
System.out.print(i+" ");
}
//Lambda表达式
list.forEach(i->{
System.out.print(i+" ");
});
功能接口是Java8中新增特性,只允许一个抽象方法。这些接口也称为单抽象方法接口。它们也可以使用Lambda表达式,方法引用和构造函数引用来表示。
Java8引入了一个注释:@FunctionalInterface
,当我们注释的接口违反了Functional Interface的规则时,会编译报错。
自定义功能接口示例:
功能接口只能有一个抽象方法,如果在被@FunctionalInterface
标注的接口中声明超过1个抽象方法,会抛出编译错误
Multiple non-overriding abstract methods found in interface …
在该接口中发现了多个未重写的抽象方法
Java8允许我们以Lambda表达式的方式为功能接口提供实现,也就是说我们可以将整个Lambda表达式作为功能接口的实现类。
例如:
功能接口
lambda表达式实现接口
Java8 内置核心四大函数式接口
其他函数式接口
Optional类可以解决空指针问题
以前处理空指针
/**
* 以前处理空指针
*/
@Test
public void test01(){
//String name = "张三";
String name = null;
if(name!=null){
System.out.println(name.length());
}else{
System.out.println("is null");
}
}
Optional类(自1.8之后)是一个没有子类的工具类,是一个值可以为Null的容器,主要作用是防止NullPointerException
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-75VzLilM-1645615215062)(C:/Users/lanlei/AppData/Roaming/Typora/typora-user-images/1645608291225.png)]
Optional的常用方法:
创建Optional实例
/**
* 创建Optional对象
*/
@Test
public void test02(){
//of(T t) 不支持null
Optional<String> op1 = Optional.of("hello");//Optional[hello]
//Optional op2 = Optional.of(null);//NullPointerException
//empty) 创建一个空的实例
Optional<String> op3 = Optional.empty();//Optional.empty
//ofNullable(T t) 支持null
Optional<String> op4 = Optional.ofNullable("hello");//Optional[hello]
Optional<String> op5 = Optional.ofNullable(null);//Optional.empty
}
其他常用方法
get() : 如果Optional有值就返回,否则抛出NoSuchElementException
异常
isPresent() : 判断对象是否包含值,包含返回true,否则返回false
orElse(T t) : 如果对象包含值,返回该值,否则返回t
orElseGet(Supplier other) : 如果对象包含值,返回改值,否则返回Lambda表达式的返回值
ifPresent() : 如果值存在则使用该值调用 consumer , 否则不做任何事情。
/**
* 常用方法
* get():如果Optional有值就返回,否则抛出`NoSuchElementException`异常
* isPresent(): 判断对象是否包含值,包含返回true,否则返回false
* orElse(T t) : 如果对象包含值,返回该值,否则返回t
* orElseGet(Supplier other) : 如果对象包含值,返回改值,否则返回Lambda表达式的返回值
*/
@Test
public void test03(){
Optional<String> op1 = Optional.of("hello");
Optional<String> op2 = Optional.empty();
if(op1.isPresent()){
System.out.println(op1.get());//hello
}
//System.out.println(op2.get());//抛出 NoSuchElementException 异常
String s1 = op1.orElse("张三");
System.out.println(s1);//hello -->对象包含值,返回包含的值
String s2 = op2.orElse("李四");
System.out.println(s2);//李四 --> 对象不包含值,返回参数值
String s3 = op2.orElseGet(() -> {
return "hello";
});
System.out.println(s3);//hello -->对象不包含值,返回lambda表达式中的返回值
}
@Test
public void test04(){
Optional<String> op1 = Optional.of("hello");
Optional<String> op2 = Optional.empty();
//如果存在值,就执行...操作
//op1.ifPresent(s-> System.out.println(s));
op1.ifPresent(System.out::println);//hello
op2.ifPresent(System.out::println);//没有值,不执行打印操作
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abc","abe"));
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
Employee employee1 = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee1));
Employee employee2 = new Employee(1001, "tom", 23, 6000);
Function<Employee,String> func2 = Employee ::getName;
System.out.println(func2.apply(employee2));
}
构造器引用要求构造器参数列表要与接口中抽象方法的参数列表一致!
且方法的返回值即为构造器对应类的对象
Java8之前接口中只能有全局常量和抽象方法
Java8引入了静态方法和默认方法
接口名称.静态方法名(参数)
Java8之前,在接口中只能声明抽象方法,不能声明有方法体的方法。
default方法是为了在原接口中扩展功能,但是对接口的实现类不会造成影响而出现的
例如Java源码中Iterable接口forEach和spliterator方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
//spliterator既可以像Iterator那样逐个迭代,也可以批量迭代。
//批量迭代可以降低迭代的开销。
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
默认方法可以被继承,如果继承多个接口,多个接口有相同的默认方法,实现类需要重写默认方法