Lambda表达式
体验lambda表达式
- 需求:启动一个线程,在控制台输出一句话:多线程启动了
- 方式1:
- 定义一个类myRunnable实现Runnable接口,重写run方法
- 创建myRunnable类的对象
- 启动Thread类的对象,把MyRunnable的对象作为构造参数传递
- 启动线程
- 方式2
- 方式3
public static void main(String[] args) {
//实现类的方式实现需求
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
//使用隐名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程启动了");
}
}).start();
//lambda表达式的方式改进
new Thread(()->{
System.out.println("多线程程序启动了");
}).start();
}
lambda表达式的标准格式
隐名内部类中重写run()方法的代码分析
- 方法形式参数为空,说明调用方法时不需要传递参数
- 方法返回值为void,说明方法执行没有结果返回
- 方法体中的内容,是我们具体要做的事情
lambda表达式的代码分析
- ():里面没有内容,可以看成是方法形式参数为空
- ->:用箭头指向后面要做的事情
- {}:包含一段diam,我们称之为代码块,可以看成是方法体中的内容
组成lambda表达式的三要素:形式参数,箭头,代码块
lambda表达式的格式
- 形式(形式参数)->{代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:由英文中划线和大于符号组成,固定写法,代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
lambda表达式的练习
- 有一个接口Eatable
- 接口中有且仅有一个抽象方法eat()
public static void main(String[] args) {
//在主方法中调用useEatable方法
EatableImpl eatable = new EatableImpl();
useEatable(eatable);
//隐名内部类
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
});
//lambda表达式
useEatable(()->{
System.out.println("一天一苹果,医生远离我");
});
}
private static void useEatable(Eatable e){
e.eat();
}
练习2:
- 定义一个接口(Flyable)里面定义一个抽象方法:void fly(String s);
- 定义一个测试类(FlyableDemo),在测试类中提供两个方法
- 一个方法是:useFlyable(Flyable f)
- 一个方法是主方法,在主方法中调用useFlyable方法
public static void main(String[] args) {
useFlyable((String s)->{
System.out.println(s);
});
}
private static void useFlyable(Flyable flyable){
flyable.fly("风和日丽,晴空万里");
}
lambda省略模式
省略规则:
- 参数类型可以省略.但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一句,可以省略大括号和分号,甚至是return;
public static void main(String[] args) {
//参数类型可以省略
useAddable((x, y) -> {return x+y;});
//但是有多个参数的情况下,不能只省略一个
// useAddable((int x, y)->{return x+y});
//如果参数有且仅有一个,那么小括号可以省略
useFlyable(s -> {
System.out.println(s);
});
//如果代码块的语句只有一条,可以省略大括号和分号
useFlyable(s -> System.out.println(s));
//如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略
useAddable(((x, y) -> x+y));
}
private static void useFlyable(Flyable f){
f.fly("风和日丽,晴空万里");
}
private static void useAddable(Addable a){
int sum = a.add(10, 20);
System.out.println(sum);
}
lambda表达式的注意事项
注意事项
- 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
- 必须有上下文环境,才能推导出Lambda对应的接口
- 根据局部变量的得知Lambda对应的接口:Runnable r = ()->System.out.println("Lambda表达式");
- 根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println("Lambda表达式").start();
Lambda表达式和隐名内部类的区别
所需类型不同
- 隐名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用隐名内部类
- 如果接口中多于一个抽象方法,只能使用隐名内部类,而不能使用Lambda表达式
实现原理不同
- 隐名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件.对应的字节码在运行的时候动态生成
体验方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方法:拿参数作操作
那么考虑一种情况:如果我们在Lambda所指定的操作方案,已经有地方存在相同方案,那么是否还有必要再写重复逻辑呢?答案是肯定没有必要
public static void main(String[] args) {
usePrintable(s -> System.out.println(s));
//方法引用符,::
usePrintable(System.out::println);
//可推导的就是可省略的
}
private static void usePrintable(Printable p){
p.printString("爱生活,爱java");
}
方法引用符
方法引用符
- ::该符号为引用运算符,而它所在的表达式内称为方法引用
- 回顾我们在体验方法引用中的代码
- Lambda表达式:usePrintable(s -> System.out.println(s));
- 分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法引用去处理
- 方法引用:usePrintable(System.out::println);
- 分析:直接使用System.out中的println方法来去掉lambda,代码更加简洁
推导与省略
- 如果使用Lambda,那么根据"可推导就是可省略"的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
public static void main(String[] args) {
//在主方法中调用usePrintable2方法
usePrintable2(i-> System.out.println(i)); //使用这个方法就是上下文环境
//方法引用
usePrintable2(System.out::println);
Printable2 p = System.out::println; //这个也是上下文环境
}
private static void usePrintable2(Printable2 printable2){
printable2.printInt(10);
}
引用类方法
引用类方法,其实就是引用类的静态方法
- 格式:类名::静态方法
- 范例:Integer::parseInt
- Integer类的方法: public static int parseInt(String s)将string转换为int类型数据
练习:
- 定义一个接口(Converter)里面定义一个抽象方法
- 定义一个测试类(ConverterDemo ),在测试类中提供两个方法
- 一个方法是:useConverter(Converter c)
- 一个是主方法,在主方法中调用useConverter方法
public static void main(String[] args) {
useConverter(s -> Integer.parseInt(s));
//引用类方法
useConverter(Integer::parseInt);
//Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
}
public static void useConverter(Converter c){
int num = c.converter("666");
System.out.println(num);
}
引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法
- 格式:对象::成员方法
- 范例: "HelloWorld"::toUpperCase (这个是错误的,因为这个没有参数),具体看例子
- String类中的方法:public String toUpperCase()将此String所有字符转换为大写
练习
- 定义一个类(PrintString)里面定义一个方法
- public void printUpper(String s):把字符串参数编程大写的数据,然后在控制台输出
- 定义一个接口(Pointer)里面定义一个抽象方法
- void printUpperCase(String s)
public class PrintString {
public void printUpper(String s) {
String s1 = s.toUpperCase();
System.out.println(s1);
}
}
- 定义测试类(PrinterDemo),在测试类中提供两个方法
- 一个方法是:usePrinter(printer p)
- 一个是主方法,在方法中调用usePrinter方法
public static void main(String[] args) {
usePrinter(s -> System.out.println(s.toUpperCase()));
//引用对象的实例方法
usePrinter(new PrintString()::printUpper);
//Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数.
}
private static void usePrinter(Printer p){
p.printUpperCase("HelloWorld");
}
引用类的实例方法
引用类的实例方法,其实就是引用类的成员方法
- 格式:类名::成员方法
- 范例: String::substring (要被实现的类必须第一个形参参数,必须为引用类的类型,参考练习)
- String类中的方法:public String substring(int beginIndex,int endIndex)
- 从beginIndex开始到endIndex结束,截取字符串,返回一个子串,子串的长度为endIndex-beginIndex
练习
- 定义一个接口(MyString),里面定义一个抽象方法:
- String mySubString(String s,int x,int y)
- 定义一个测试类(MyStringDemo),在测试类中提供两个方法
- 一个方法是useMyString(MyString s)
- 一个方法是主方法,在主方法中调用useMyString方法
public static void main(String[] args) {
//lambda
useMyString((s, x, y) -> s.substring(x,y));
//调用应用类的实例方法
useMyString(String::substring);
//Lambda表达式被类的实例方法替代时候
//第一个参数作为调用者
//后面的参数全部传递给该方法作为参数
}
private static void useMyString(MyString s){
String helloworld = s.myString("helloworld", 2, 5);
System.out.println(helloworld);
}
//Lambda表达式被类的实例方法替代时候
//第一个参数作为调用者
//后面的参数全部传递给该方法作为参数
在来一个例子加强:
public class Student {
private String name;
private int age;
public Student build(String name,int age){
return new Student(name,age);
}
}
public interface StringBuilder2 {
Student build(Student student ,String name,int age);
}
StringBuilder2 builder2 = Student::build;
System.out.println(builder2.build(new Student(),"小青",20));
引用构造器
引用构造器,其实就是引用构造方法
- 格式:类名::new
- 范例:Student::new
练习
- 定义一个类(Student),里面有两个成员变量(name,age)
- 提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
public interface StudentBuilder {
Student build(String name, int i);
}
- 定义一个接口(StudentBuilder),里面定义一个抽象方法
public interface StudentBuilder { Student build(String name, int i); }
- 定义一个测试类(StudentDemo),在测试类中提供两个方法
- 一个方法是useStudentBuilder(StudentBuilder s)
- 一个是主方法,在主方法中调用useStudentBuilder方法
public static void main(String[] args) {
//lambda
useStudentBuilder((s,a)->new Student(s,a));
//引用构造器
useStudentBuilder(Student::new);
//Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
}
public static void useStudentBuilder(StudentBuilder b){
Student student = b.build("林青霞", 20);
System.out.println(student);
}
函数式接口
描述
函数式接口:有且仅有一个抽象方法的接口
java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口
只有确保接口中有且仅有一个抽象方法,java中的Lambda才能顺利地进行推导
如何检测一个接口是不是函数式接口呢?
- @FunctionalInterface
- 放在接口定义的上方:如果接口是函数式接口,编译通过,如果不是,编译失败
注意:
- 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注释,只要包中满足函数式接口定义的条件,也照样是函数式接口.但是,建议加上该注解
函数式接口作为方法的参数
需求
- 定义一个类(RunnableDemo)在类中提供两个方法
- 一个方法是:startThread(RUnnable r)方法参数Runnable是一个函数式接口
- 一个方式是主方法,在主方法中调用startThread方法
public static void main(String[] args) {
//采用隐名内部类的方式
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程启动了");
}
});
startThread(()->System.out.println(Thread.currentThread().getName()+"线程启动了"));
}
private static void startThread(Runnable r){
Thread thread = new Thread(r);
thread.start();
}
}
函数式接口作为方法的返回值
需求
- 定义一个类(ComparatorDemo),在类中提供方法
- 一个方法是:Comparator getComparator()方法返回值Comparator是一个函数式接口
- 一个方法是主方法,在主方法中调用getComparator方法
- 如果方法的返回值是一个函数式接口,我们可以是还有Lambda表达式作为结果返回
public class ComparatorDemo {
public static void main(String[] args) {
//构造使用的场景;
//定义集合,存储字符串元素
ArrayList<String> strings = new ArrayList<>();
strings.add("abb");
strings.add("bbbb");
strings.add("ddddddd");
System.out.println(strings);
Collections.sort(strings,getComparator());
System.out.println(strings);
}
private static Comparator<String> getComparator(){
// return new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// return o2.length()-o1.length();
// }
// };
return (s,x)->s.length()-x.length();
}
}
常用的函数式接口
java8在java.util..function包下预定义了大量的函数式接口供我们实现
Supplier接口
Supplier接口:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由lambda表达式实现)返回一个数据
- Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
public static void main(String[] args) {
String string = getString(() -> "林青霞");
System.out.println(string);
Integer integer = getInteger(() -> 30);
System.out.println(integer);
}
//定义一个方法,返回一个整数数据
private static Integer getInteger(Supplier<Integer> supplier){
return supplier.get();
}
//定义一个方法,返回一个字符串数据
public static String getString(Supplier<String> sup){
return sup.get();
}
Consumer:包含两个方法
- void accept(T t)对给定的参数执行此操作
- default Consumer andThen(Consumer after):返回一个组合的Consumer,执行此操作,然后执行after操作
- Consumer 接口也被称为消费型接口,它消费的数据类型由泛型指定
public static void main(String[] args) {
operatorString("林青霞",System.out::println);
operatorString("林青霞",s -> System.out.println(new StringBuilder(s).reverse().toString()));
System.out.println("-------------------------");
operatorString1("林青霞",s -> System.out.println(s),s -> new StringBuilder(s).reverse().toString());
}
//定义一个方法,用不同的方法消费同一个字符串消费同一个字符串数据两次
private static void operatorString1(String name, Consumer<String> con,Consumer<String> con2){
con.accept(name);
con2.andThen(con2).accept(name);
}
//定义一个方法,消费一个字符串数据
private static void operatorString(String name, Consumer<String> con){
con.accept(name);
}
Predicate接口
Predicat:常用四个方法
- boolean test(T t):对给定的参数进行判断(判断逻辑由lambda表达式实现)返回一个布尔值
- default Predicate negate():返回一个逻辑的否定,对应逻辑非
- default Predicate and(Predicate other) :返回一个组合判断,对应短路与
- default Predicate or(Predicate other):返回一个组合判断.对应短路或
- Predicate接口通常用于判断参数是否满足指定的条件
public static void main(String[] args) {
boolean b0 = checkString("hello", s -> s.length() > 8);
boolean b1 = checkString("hello2222222222", s -> s.length() > 8);
System.out.println(b0);
System.out.println(b1);
System.out.println(checkString2("123456952",s -> s.length()>3,s -> s.length()<8));
}
//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> predicate){
//negate否定
return predicate.negate().test(s);
}
//判断给定的字符串是否满足要求
private static boolean checkString2(String s, Predicate<String> predicate, Predicate<String> predicate2){
return predicate.and(predicate2).test(s);
}
练习
public static void main(String[] args) {
//筛选出姓名长度大于2,年龄大于33
String[] strings = {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};
cheekPerson(strings,s -> s.split(",")[0].length()>2,s -> s.split(",")[1].compareTo("33")>0);
}
private static void cheekPerson(String[] strings, Predicate<String> p1,Predicate<String> p2){
for (String s :strings){
boolean test = p1.and(p2).test(s);
if (test){
System.out.println(s);
}
}
}
Function接口
Function:常用两个方法(T是参数,T是返回类型)
- R apply(T t):将次函数应用于给定的参数
- default Function andThen(Function after):返回一个组合函数,首先将该函数引用与输入,然后将after函数应用于结果
- Function接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新值.
public static void main(String[] args) {
convert("123",s -> Integer.parseInt(s));
convert("567",Integer::parseInt);
convert2(123,integer -> String.valueOf(integer+566));
convert3("1",s -> Integer.parseInt(s)+3,String::valueOf);
}
//定义一个方法,把一个字符串转换成int类型,在控制台输出
private static void convert(String s, Function<String,Integer> func){
Integer apply = func.apply(s);
System.out.println(apply);
}
//定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert2(Integer i, Function<Integer,String> func){
String apply = func.apply(i);
System.out.println(apply);
}
//定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert3(String s, Function<String,Integer> func,Function<Integer,String> func2){
// Integer apply = func.apply(s);
// String apply1 = func2.apply(apply);
// System.out.println(apply1);
String apply = func.andThen(func2).apply(s);
System.out.println(apply);
}
练习
/*
String s = "林青霞,30,
请按我指定的要求进行操作:
1.将字符串截取得到数字年龄部分
2.将上一步的年龄转换称为int类型的数据
3.将上一步的int数据加70,得到一个int结果,在控制台输出
*/
public static void main(String[] args) {
convert("林青霞,30",s -> s.split(",")[1], //截取年龄
s -> Integer.parseInt(s),//将字符串转为int类型
integer -> integer+70); //将数字加70
}
private static void convert(String s, Function<String,String> fun1,Function<String,Integer> fun2, Function<Integer,Integer> fun3){
Integer apply = fun1.andThen(fun2).andThen(fun3).apply(s);
System.out.println(apply);
}
接口
接口组成更新描述
接口的组成
- 常量
- 抽象方法
- 静态方法(java8)
- 默认方法(java8)
- 私有方法(java9)
默认方法
接口默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表){}
- 范例:public default void show3(){可以有代码}
public interface MyInterface {
void show1();
void show2();
// void show3();
public default void show3(){
System.out.println("sxa");
}
接口中静态方法
接口中静态方法的定义格式
- 格式:public static 返回值类型 方法名(参数列表){}
- 范例:public static void show(){}
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略;
接口中私有方法
java9中新增了带方法体的私有方法,这其实在java8就埋下了伏笔:java8允许在接口中定义带方法的默认方法和静态方法.这样可能就引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别使用的,因此用私有给隐藏起来,这就是java9增加私有方法的必然性
接口中私有方法的定义格式:
- 格式1:private 返回值类型 方法名(参数列表){}
- 范例1:private void show(){}
- 格式2:private static 返回值类型 方法名(参数列表){}
- 范例2:private static void method(){}
接口中私有方法的注意事项:
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
Stream流
体验Stream流
需求:
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//把集合中所有以"张"开头的元素存储到一个新的集合
ArrayList<String> zhangList = new ArrayList<>();
for (String s:list){
if (s.startsWith("张")){
zhangList.add(s);
}
}
System.out.println(zhangList);
//把"张"开头的集合汇总的长度为3的元素存储到一个新的集合
ArrayList<String> threeList = new ArrayList<>();
for (String zhang: zhangList){
if (zhang.length()==3){
threeList.add(zhang);
}
}
// System.out.println(threeList);
//遍历上一步去得到的集合
threeList.forEach(s -> System.out.println(s));
System.out.println("------------");
//Stream流进行改写
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s -> System.out.println(s));
}
使用Stream流的方式完成过滤操作
- list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s -> System.out.println(s));
- 直接阅读代码的字面意思即可完美展示无关逻辑方法的语义:生成流,过滤张性,过滤长度为3,逐一打印
- Stream流把真正的函数式编程风格引入到java中
Stream流的生成方式
Stream流的使用
- 生成流
- 通过数据源(集合,数组等)生成流
- list.stream()
- 中间操作
- 一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用
- filter()
- 终结操作
- 一个流只能有一个终结操作,党政操作执行后,流就被使用"光"了,无法再被操作,所以这必定是流的最后一个操作
- forEach()
Stream流的常见生成方式
- Collection体系的集合可以使用默认方法stream()生成流
- Map体系的集合间接的生成流
- 数组可以通过Stream接口的静态方法of(T...values)生成流
//Collection体系的集合可以使用默认方法stream()生成流
ArrayList<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
HashSet<String> strings = new HashSet<>();
Stream<String> stream1 = strings.stream();
//Map体系的集合间接生成流
HashMap<String, String> stringStringHashMap = new HashMap<>();
Stream<String> keyStream = stringStringHashMap.keySet().stream();
Stream<String> stream2 = stringStringHashMap.values().stream();
Stream<Map.Entry<String, String>> stream3 = stringStringHashMap.entrySet().stream();
//数组可以通过Stream接口的静态方法of(T...values)生成流
String[] array={"hello","world","java"};
Stream<String> array1 = Stream.of(array);
Stream<Integer> integerStream = Stream.of(10, 20, 20, 30);
Stream filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法 Boolean test(T t) 对给定的参数进行判断,返回一个布尔值
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求:把list集合中以张开头的元素在控制台输出
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
//需求,list集合中长度为3的元素在控制台输出
list.stream().filter(s -> s.length()==3).forEach(System.out::println);
//把list集合中以张开头的,长度为3的元素在控制台输出
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(System.out::println);
}
limit和skip方法
Stream limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据
Stream skip(long n):跳过指定参数个数的数据,返回该留的剩余元素组成的流
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//取前三个数据在控制台输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("--------");
//跳过前三个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
System.out.println("-------");
//跳过2个元素,把剩下的元素中前2个在控制台输出
list.stream().skip(2).limit(2).forEach(System.out::println);
}
concat和distinct方法
static Stream concat(Stream a,Stream b):合并a和b两个流为一个流
Stream distinct():返回由该流的不同元素(根据Object.equals(Object))组成的流
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1,取前4个数据组成一个流
Stream<String> limit = list.stream().limit(4);
//需求2,取跳过2个数据组成一个流
Stream<String> skip = list.stream().skip(2);
//需求3,合并需求1和需求2得到的流,并把结果输出
// Stream.concat(limit,skip).forEach(System.out::println);
//需求4,合并需求1和需求2得到的流,并把结果输出在控制台,要求字符串元素不能重复
Stream.concat(limit,skip).distinct().forEach(System.out::println);
}
sorted方法
- Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("linqinxia");
list.add("zhangmanyu");
list.add("wangzuxian");
list.add("liuyan");
list.add("zhangmin");
list.add("zhnagwuji");
//按照字母顺序把数据在控制台输出
list.stream().sorted((x,y)->x.compareTo(y)).forEach(System.out::println);
System.out.println("---------------");
//按照字母长度把数据在控制台输出
list.stream().sorted((x,y)->x.length()-y.length()).forEach(System.out::println);
}
map和mapToInt方法
- Stream map(Function mapper):返回给定函数应用于此流的元素的结果组成的流
- IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
- IntStream :表示原始int流
- ToIntFunction接口中的方法 int applyAsInt(T value)
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("20");
list.add("30");
list.add("15");
list.add("56");
list.add("40");
list.add("10");
//需求:将集合中的字符串数据转换为整数之后在控制台输出
list.stream().map(Integer::parseInt).forEach(System.out::println);
list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
//int sum()返回此流中元素的总和
int sum = list.stream().mapToInt(Integer::parseInt).sum();
}
forEach和count方法
Stream流的常见终结操作方法
- void forEach(Consumer action):对此流的每个元素执行操作
- Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
- long count():返回此流中的元素数
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:把集合中的元素在控制台输出
list.stream().forEach(System.out::println);
//统计集合中以张开头的元素
long 张 = list.stream().filter(s -> s.startsWith("张")).count();
System.out.println(张);
}
练习
现有两个ArrayList集合,分布存储6名男演员名称和女演员名称,要求完成如下操作
- 男演员只要名字为3个字的前三人
- 女演员只有姓林的,并且不要第一个
- 把过滤后的男演员姓名和女演员姓名合并到一起
- 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
- 演员类Actor,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
public static void main(String[] args) {
ArrayList<String> man = new ArrayList<>();
man.add("周仁发");
man.add("成龙");
man.add("刘德华");
man.add("吴京");
man.add("周星驰");
man.add("李连杰");
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//男演员只有名字为3个字的前三人
Stream<String> limit = man.stream().filter(s -> s.length() == 3).limit(3);
//女演员只要姓林的,并且不要第一个人
Stream<String> lin = list.stream().filter(s -> s.startsWith("林")).skip(1);
//把过滤后的男演员姓名和女演员姓名合并到一起
Stream<String> concat = Stream.concat(lin, limit);
//把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
concat.map(Actor::new).forEach(System.out::println);
}
package seventh;
/**
* @author keep
* @create 2022/4/17
*/
public class Actor {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Actor(String name) {
this.name = name;
}
public Actor() {
}
@Override
public String toString() {
return "Actor{" +
"name='" + name + '\'' +
'}';
}
}
流的收集
对数据使用Stream流的方式操作完成后,把流的数据收集到集合中
Stream流 的收集方法
- R collect(Collector collector)
- 收集方法的参数是一个Collector接口
工具类Collectors提供了具体的收集方式
- public static Collector list():把元素收集到list集合中
- public static Collector toSet()把元素收集到Set集合中
- public static Collector toMap(Function keyMapper,Function valueMaooer):把元素收集到集合中
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//得到名字为3个字的流
Stream<String> stringStream = list.stream().filter(s -> s.length() == 3);
//使用Stream流操作完毕的数据收集到list集合中并遍历
List<String> collect = stringStream.collect(Collectors.toList());
collect.forEach(System.out::println);
//创建set集合对象
HashSet<Integer> integers = new HashSet<>();
integers.add(10);
integers.add(20);
integers.add(30);
integers.add(40);
//得到年龄大于25的流
Stream<Integer> integerStream = integers.stream().filter(integer -> integer > 25);
//把使用Stream流操作完毕的数据收集到set集合中并遍历
Set<Integer> collect1 = integerStream.collect(Collectors.toSet());
collect1.forEach(System.out::println);
//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray={"林青霞,30","张曼玉,35","王祖贤,33","小雪,20"};
//得到字符串年龄数据大于25的流
Stream<String> stringStream1 = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 25);
//使用Stream流操作完成的数据收集到Map集合中并遍历,字符串中的姓名作为键,年龄作值
Map<String, Integer> collect2 = stringStream1.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
collect2.forEach((x,y)->{
System.out.println(x+y);
});
?
|