java回顾 Lambda表达式和Stream流
Lambda表达式
函数式编程思想概述
面向对象思想:
做一件事情,找一个能解决这个事情的对象,调用对象的方法来完成这件事情
函数式的编程思想:
重视的是结果,怎么做事情,不重视完成的过程,找谁做
使用Lambda表达式简化匿名内部类
Lambda表达式的标准格式与省略格式(重点)
()->{}
(参数)->{重写抽象方法的方法体}
(int a,int b)->{}>省略数据类型 (a,b)->{}
(int a)->{}>省略数据类型 a->{}
a->{只有一行代码}==>a->代码(return ; {} 一起省略)
public class Demo01Lambda {
public static void main(String[] args) {
//使用匿名内部类创建一个新的线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->使用匿名内 部类的方式,实现多线程程序!");
}
}).start();
//使用Lambda表达式简化匿名内部类的书写
new Thread(()->{
System.out.println(Thread.currentThread()..getName()+"使用Lambda表达式,实现多线程程序")
}).start();
}
}
Lambda表达式的语法
Lambda表达式的语法:
由一些参数,一个箭头,一些代码组成
格式:
写的是@Override里的方法
(参数列表)->{重写抽象方法的方法体}
Lambda表达式作用:简化匿名内部类
lambda表达式使用前提:必须有接口,接口中有且只能有一个抽象方法(函数式接口)
Lambda表达式是可推导,可省略:能推导出来,Lambda表达式重写的就是接口中唯一的抽象方法
- (参数列表):重写抽象方法的参数
- ->:传递(固定写法):可以把参数传递到方法体中使用
- {}:重写抽象方法的方法体
使用Lambda表达式重写有参数有返回值的方法
/*
使用Lambda表达式重写有参数有返回值的方法
需求:
创建一个数组,数据类型使用Person
创建3个Person对象,存储到数组中
使用Arrays数组工具类中的sort方法,根据比较器产生的比较规则对Person对象进行排序(年龄升序)
*/
public class Demo02Lambda {
public static void main(String[] args) {
//创建一个数组,数据类型使用Person
Person[] arr = new Person[3];
//创建3个Person对象,存储到数组中
arr[0] = new Person("张三",18);
arr[1] = new Person("李四",20);
arr[2] = new Person("王五",19);
System.out.println("排序前,数组中的元素:"+ Arrays.toString(arr));
//使用Arrays数组工具类中的sort方法,根据比较器产生的比较规则对Person对象进行排序(年龄升序)
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
//升序:o1-o2 降序:o2-o1
return o1.getAge()-o2.getAge();
}
});
System.out.println("排序后,数组中的元素:"+ Arrays.toString(arr));
//使用lambda表达式简化匿名内部类
Arrays.sort(arr,(Person o1, Person o2)->{
return o2.getAge()-o1.getAge();
});
System.out.println("排序后,数组中的元素:"+ Arrays.toString(arr));
}
}
Lambda表达式简化格式
/*
Lambda表达式简化格式:
Lambda表达式的使用前提:有接口,接口中有且仅有一个抽象方法
Lambda表达式是可推导,可省略的
可以推导出,Lambda表达式重写的就是接口中唯一的抽象方法
也可以推导出方法的参数和方法有没有返回值,所以可以对参数和返回值在进行简化
格式:
(参数列表)->{重写抽象方法的方法体}
1.(参数列表):参数列表的数据类型是可以推导出来的,可以省略
(int a)-->(a)
(int a,String s)-->(a,s)
2.(参数列表):参数列表中的参数只有一个,()括号也可以省略,但是参数列表没有参数()不能省略
(int a)-->(a)-->a
()-->不能省略()
3.{重写抽象方法的方法体}:重写的方法体,只有一行代码,无论是否有返回值
{ }和一行代码的分号和return关键字都可以省略,但是必须一起省略
*/
public class Demo03Lambda {
public static void main(String[] args) {
//使用Lambda表达式简化实现多线程的匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类的方式");
}
}).start();
new Thread(()->{
System.out.println("lambda表达式的方式");
}).start();
//简化Lambda表达式
new Thread(()->System.out.println("lambda表达式的方式")).start();
System.out.println("---------------------------");
//创建一个数组,数据类型使用Person
Person[] arr = new Person[3];
//创建3个Person对象,存储到数组中
arr[0] = new Person("张三",18);
arr[1] = new Person("李四",20);
arr[2] = new Person("王五",19);
System.out.println("排序前,数组中的元素:"+ Arrays.toString(arr));
//使用lambda表达式简化匿名内部类
Arrays.sort(arr,(Person o1, Person o2)->{
return o2.getAge()-o1.getAge();
});
System.out.println("排序后,数组中的元素:"+ Arrays.toString(arr));
//简化Lambda表达式
Arrays.sort(arr,(o1,o2)-> o2.getAge()-o1.getAge());
}
}
Stream流
Stream流的基本概述
我们可以把集合|数组,转换为Stream流,使用Stream流中的方法,对集合|数组进行操作
Stream流的基本体验
使用传统方法对集合进行操作
/*
使用传统技术对集合进行操作
*/
public class Demo01ArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
//1. 首先筛选所有姓张的人;把姓张的人存储到一个新的集合中
List<String> zhangList = new ArrayList<>();
for (String name : list) {
if(name.startsWith("张")){
zhangList.add(name);
}
}
//System.out.println(zhangList);//[张无忌, 张强, 张三丰]
//2. 然后筛选名字有三个字的人;把名字有三个字的人存储到一个新的集合中
List<String> threeList = new ArrayList<>();
for (String name : zhangList) {
if(name.length()==3){
threeList.add(name);
}
}
//System.out.println(threeList);//[张无忌, 张三丰]
//3. 最后进行对结果进行打印输出。
for (String name : threeList) {
System.out.println(name);
}
}
}
使用stream流对集合进行操作
/*
使用传统技术对集合进行操作
*/
public class Demo01ArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
//1. 首先筛选所有姓张的人;把姓张的人存储到一个新的集合中
//2. 然后筛选名字有三个字的人;把名字有三个字的人存储到一个新的集合中
//3. 最后进行对结果进行打印输出。
//把集合转换为Stream流
list.stream()
.filter(name->name.startWith("张"))//判断是否以张开头
.filter(name0>name.length()==3)//判断元素是否等于3
.forEach(name->System.out.println(name));//遍历集合
}
}
流式思想概述
获取stream流的方式(重点)
/*
获取Stream流的方式(重点)
1.在Collection接口中,定义了一个方法stream,可以把集合转换为Stream流
default Stream<E> stream() 此方法只能是Collection接口下的单列集合可以使用
2.使用Stream接口中的方法of,可以把可变参数(数组)转换为Stream流
static <T> Stream<T> of(T... values) 方法的参数是一个可变参数,可以传递数组
*/
public class Demo03Stream {
public static void main(String[] args){
show02();
}
/*
把数组转换为Stream流
注意:
可变参数,可以传递任意个
*/
private static void show02(){
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
Stream<String> stream2 = Stream.of("aaa", "bbb", "ccc");
//可变参数底层就是一个数组,of方法参数也可以传递数组
String[] arr1 = {"aaa", "bbb", "ccc"};
Stream<String> stream3 = Stream.of(arr1);
//注意:数组的类型必须使用包装类
Integer[] arr3 = {1,2,3};
Stream<Integer> stream5 = Stream.of(arr3);
}
/*
把集合转换为Stream流
*/
private static void show01() {
ArrayList<Integer> list = new ArrayList<>();
//把ArrayList集合转换为Stream流
Stream<Integer> stream1 = list.stream();
LinkedList<String> linked = new LinkedList<>();
//把LinkedList集合转换为Stream流
Stream<String> stream2 = linked.stream();
HashSet<Double> set = new HashSet<>();
//把HashSet集合转换为Stream流
Stream<Double> stream3 = set.stream();
HashMap<String,String> map = new HashMap<>();
//map.stream();//Cannot resolve method 'stream()' Map集合是不能直接转换为Stream流
Set<String> keySet = map.keySet();
//把Map集合中所有的key转换为Stream流
Stream<String> stream4 = keySet.stream();
Collection<String> values = map.values();
//把Map集合中所有的value转换为Stream流
Stream<String> stream5 = values.stream();
Set<Map.Entry<String, String>> entries = map.entrySet();
//把Map集合中所有的Entry对象转换为Stream流
Stream<Map.Entry<String, String>> stream6 = entries.stream();
}
}
Stream的常用方法
流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
-
终结方法:返回值类型不再是
Stream
接口自身类型的方法,因此不再支持类似StringBuilder
那样的链式调用。本小节中,终结方法包括count
和forEach
方法。 -
非终结方法:返回值类型仍然是
Stream
接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法。)
函数拼接与终结方法
在上述介绍的各种方法中,凡是返回值仍然为Stream
接口的为函数拼接方法,它们支持链式调用;而返回值不再为Stream
接口的为终结方法,不再支持链式调用。如下表所示:
备注:本小节之外的更多方法,请自行参考API文档。
forEach方法:用于遍历
/*
Stream流中常用方法
forEach:用于遍历Stream流中的元素
void forEach(Consumer<? super T> action) 对此流的每个元素执行操作。
参数:
Consumer<? super T> action:是一个消费型接口
抽象方法:
void accept(T t) :消费一个指定泛型类型的数据
注意:
forEach方法是一个终结方法,没有返回值;也不能使用链式编程继续调用Stream流中的其他方法了
*/
public class Demo04forEach {
public static void main(String[] args) {
//获取Stream流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//使用forEach方法遍历Stream流中的元素
stream.forEach((Integer in)->{
System.out.println(in);
});
/*
IllegalStateException: stream has already been operated upon or closed 非法状态异常
Stream流只能使用一次,使用完毕就会流向到下一个Stream流对象
之前的Stream流对象就会被销毁,就不能在使用了,否则会抛出异常
*/
//简化Lambda表达式
stream.forEach(i-> System.out.println(i));
}
}
Count方法:统计个数
/*
Stream流中常用方法
count:用于统计Stream流中元素个数
long count();
注意:
count方法是一个终结方法,返回值类型是long,不能继续调用Stream中的方法了
*/
public class Demo05count {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
//把ArrayList集合转换为Stream流
Stream<String> stream = list.stream();
//使用Stream流中的count方法,统计元素个数
long count = stream.count();
System.out.println(count);//4
}
}
filter方法:过滤
/*
Stream流中常用方法
filter:用于过滤Stream流中的元素
Stream<T> filter(Predicate<? super T> predicate);
参数:
Predicate<? super T> predicate接口
抽象方法:
boolean test(T t) 用于对接口指定泛型类型的数据进行判断
注意:
filter方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
*/
public class Demo06filter {
public static void main(String[] args) {
//创建一个Stream流
Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊", "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
//使用filter方法过滤Stream流中的元素,只要包含羊羊的
/* Stream<String> stream2 = stream.filter((String s) -> {
return s.contains("羊羊");
});
//遍历stream2流
stream2.forEach(s-> System.out.println(s));*/
//链式编程
stream.filter(s->s.contains("羊羊")).forEach(s-> System.out.println(s));
}
}
limit方法:获取前n个元素
/*
Stream流中常用方法
limit:获取前n个元素
Stream<T> limit(long maxSize);
注意:
limit方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
*/
public class Demo07limit {
public static void main(String[] args) {
Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊",
"慢羊羊", "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
//使用limit方法,获取Stream流中的前4个元素,存储到一个新的Stream流中返回
//Stream<String> stream2 = stream.limit(4);
//遍历stream2流
//ream2.forEach(s-> System.out.println(s));
//链式编程
stream.limit(4).forEach(s -> System.out.println(s));
}
}
skip方法:跳过前n个元素
/*
Stream流中常用方法
skip:跳过前n个元素
Stream<T> skip(long n);
注意:
1.skip方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
2.skip跳过的元素数量大于流中元素的数量,返回一个没有元素的空流
*/
public class Demo08skip {
public static void main(String[] args) {
//创建Stream流对象
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//使用skip方法跳过前6个元素,把剩余的元素存储到一个新的Stream流中返回
//Stream<Integer> stream2 = stream.skip(6);
//遍历stream2
//ream2.forEach(s-> System.out.println(s));
//ream.skip(6).forEach(s-> System.out.println(s));
long count = stream.skip(6).count();
System.out.println(count);//4
}
}
map方法:映射类型转换
/*
Stream流中常用方法
map方法:映射,把Stream流中的元素,转换为另外一种数据类型的流(类型转换)
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
参数:
Function<T, R> mapper
抽象方法:
R apply(T t),根据类型T的参数获取类型R的结果。用于数类型转换
注意:
map方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
*/
public class Demo09map {
public static void main(String[] args) {
Stream<String> stream = Stream.of("11", "22", "33", "44");
//使用Stream流中map方法,把字符串类型的流,转换为Integer类型的流返回
/*Stream<Integer> integerStream = stream.map((String s) -> {
return Integer.parseInt(s);
});*/
//遍历Stream流
//integerStream.forEach(s-> System.out.println(s+10));
//链式编程
stream.map(s->Integer.parseInt(s)).forEach(s-> System.out.println(s));
}
}
concat方法:组合
/*
Stream流中常用方法
concat方法:组合,可以把两个Stream流,组合为一个新的Stream流
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
注意:
concat方法的返回值类型是Stream,是一个非终结方法,可以使用返回的Stream继续调用Steam流中的其他方法
*/
public class Demo10concat {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊", "暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰");
Stream<String> stream2 = Stream.of("aa", "bb", "cc", "dd", "ee");
//使用Stream中的静态方法concat把两个Stream组合为一个新的Stream流
Stream<String> stream = Stream.concat(stream1, stream2);
//遍历Stream流
stream.forEach(s-> System.out.println(s));
Stream<String> stream3 = Stream.of("aa", "bb", "cc", "dd", "ee");
Stream<Integer> stream4 = Stream.of(1,2,3,4);
//Stream<Object> concat = Stream.concat(stream3, stream4);
//concat.forEach(s-> System.out.println(s));
Stream.concat(stream3,stream4).forEach(s-> System.out.println(s));
}
}
使用stream流和不使用stream流的区别
不使用stream流
/*
现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
5. 将两个队伍合并为一个队伍;
6. 打印整个队伍的姓名信息。
*/
public class Demo01StreamTest {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("老子");
one.add("庄子");
one.add("孙子");
one.add("洪七公");
one.add("欧阳锋");
one.add("蔡徐坤");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("张三丰");
two.add("赵丽颖");
two.add("张二狗");
two.add("张天爱");
two.add("张三");
//1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
List<String> one1 = new ArrayList<>();
for (String s : one) {
if(s.length()==3){
one1.add(s);
}
}
//System.out.println(one1);//[宋远桥, 苏星河, 洪七公, 欧阳锋, 蔡徐坤]
//2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
List<String> one2 = new ArrayList<>();
for (int i = 0; i < 3; i++) {//i=0,1,2
one2.add(one1.get(i));
}
//System.out.println(one2);//[宋远桥, 苏星河, 洪七公]
//3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
List<String> two1 = new ArrayList<>();
for (String s : two) {
if(s.startsWith("张")){
two1.add(s);
}
}
//System.out.println(two1);//[张无忌, 张三丰, 张二狗, 张天爱, 张三]
//4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
List<String> two2 = new ArrayList<>();
for (int i = 2; i < two1.size(); i++) {
two2.add(two1.get(i));
}
//System.out.println(two2);//[张二狗, 张天爱, 张三]
//5. 将两个队伍合并为一个队伍;
List<String> all = new ArrayList<>();
//Collection集合中的方法boolean addAll(Collection<? extends E> c) 把一个集合中的所有元素,添加到另外一个集合中
all.addAll(one2);
all.addAll(two2);
//6. 打印整个队伍的姓名信息。
for (String s : all) {
System.out.println(s);
}
}
}
使用stream流
/*
现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
5. 将两个队伍合并为一个队伍;
6. 打印整个队伍的姓名信息。
*/
public class Demo02StreamTest {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("老子");
one.add("庄子");
one.add("孙子");
one.add("洪七公");
one.add("欧阳锋");
one.add("蔡徐坤");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("张三丰");
two.add("赵丽颖");
two.add("张二狗");
two.add("张天爱");
two.add("张三");
//1. 第一个队伍只要名字为3个字的成员姓名;3个字的成员姓名存储到一个新集合中
//2. 第一个队伍筛选之后只要前3个人;前3个人存储到一个新集合中
//把one集合转换为Stream流
Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);
//3. 第二个队伍只要姓张的成员姓名;姓张的成员存储到一个新集合中
//4. 第二个队伍筛选之后不要前2个人;跳过之后的成员存储到一个新集合中
//把two集合转换为Stream流
Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);
//5. 将两个队伍合并为一个队伍;
//6. 打印整个队伍的姓名信息。
Stream.concat(oneStream,twoStream).forEach(s-> System.out.println(s));
}
}
收集Stream结果(重点)
把Stream流转换为集合或者把Stream流转换为数组
把Stream流转换为集合:收集到集合中
*
把Stream流转换为集合:收集到集合中
使用Stream流中的方法:
R collect(Collector<? super T,A,R> collector)
参数:
Collector:是一个接口,需要传递接口的实现类对象
java.util.stream.Collectors是一个工具类,里边提供的方法,返回了Collector接口的实现类对象
static <T> Collector<T,?,List<T>> toList(): 把Stream流收集到List集合中
static <T> Collector<T,?,Set<T>> toSet() : 把Stream流收集到Set集合中
*/
public class Demo01Stream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊",
"暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰","美羊羊");
//把Stream流收集到List集合中:转换为List集合(有序,有索引,允许重复)
//List<String> list = stream.collect(Collectors.toList());
//System.out.println(list);//[美羊羊, 喜羊羊, 懒羊羊, 慢羊羊, 暖羊羊, 沸羊羊, 灰太狼, 红太狼, 小灰灰]
//把Stream流收集到Set集合中:转换为Set集合(无序,不包含索引的方法,不允许存储重复元素)
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);//[美羊羊, 沸羊羊, 红太狼, 灰太狼, 暖羊羊, 小灰灰, 喜羊羊, 懒羊羊, 慢羊羊]
}
}
把Stream流转换为数组:收集到数组中
/*
把Stream流转换为数组:收集到数组中
Stream流中的方法:
Object[] toArray()
*/
public class Demo02Stream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("美羊羊", "喜羊羊", "懒羊羊", "慢羊羊",
"暖羊羊", "沸羊羊", "灰太狼", "红太狼", "小灰灰","美羊羊");
Object[] arr = stream.toArray();
for (Object o : arr) {
System.out.println(o);
}
}
}
文章版权声明:除非注明,否则均为彭超的博客原创文章,转载或复制请以超链接形式并注明出处。
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。