🎄Stream API Java8引入
🍭概述
在Java中,流(Stream)是指Java 8中引入的Stream API,它是一种用于操作集合数据的高级抽象。
Java Stream API基于函数式编程的思想,提供了一组丰富的操作方法,用于对集合数据进行过滤、映射、排序、归约等操作,以及支持并行处理。
Java Stream API的使用步骤如下:
- 创建流:通过集合类的
stream()
方法或parallelStream()
方法创建流对象。也可以通过Stream.of()
方法从一组对象创建流,或者通过Arrays.stream()
方法从数组创建流。 - 中间操作:通过调用流对象上的各种中间操作方法,对流进行操作。常见的中间操作方法包括
filter()
、map()
、distinct()
、sorted()
等,用于对流进行过滤、映射、去重、排序等操作。这些操作可以顺序地串联起来,形成一个操作链。 - 终端操作:通过调用流对象上的终端操作方法,对流进行最终的计算和结果返回。常见的终端操作方法包括
forEach()
、collect()
、reduce()
、count()
等,用于对流进行遍历、收集结果、归约计算等操作。
Java Stream API的使用优势包括:
- 函数式编程风格:Java Stream API利用Lambda表达式和方法引用,提供了一种简洁、直观的集合操作方式,增强了代码的可读性和可维护性。
- 延迟执行:流操作通常是延迟执行的,只有在终端操作调用时才会进行实际的计算,这样可以节省计算资源。
- 并行处理:Java Stream API支持并行处理,可以自动将流的操作分配到多线程执行,提高处理数据的效率。
🍭使用场景
- 数据处理与转换:Stream API提供了丰富的操作方法,可以方便地对数据进行过滤、映射、排序、归约等操作。这些操作可以灵活组合,使得数据处理和转换更加简洁高效。例如,可以通过流操作筛选出符合某个条件的数据,然后对结果进行进一步的转换和计算。
- 数据统计与分析:利用Stream API可以快速对数据集合进行统计分析。通过调用
count()
、sum()
、max()
、min()
等终端操作方法,可以获取数据的数量、总和、最大值、最小值等结果。同时,可以结合中间操作方法如filter()
、map()
等进行数据筛选和转换,用于特定的统计需求。 - 并行处理:Stream API提供了并行处理的支持,可以自动将流的操作分配到多线程执行,提高数据处理的效率。适用于对大规模数据集合进行并行计算的场景,例如大数据处理、图像处理等。
- 集合操作优化:Stream API提供了优化的集合操作,可以避免传统的迭代方式对集合进行繁琐的遍历和操作。通过链式调用操作方法,可以将一系列的集合操作简化为一行代码,提高代码的可读性和可维护性。同时,Stream API也提供了延迟执行的特性,只在需要返回结果时才进行实际计算,可以节省计算资源。
- 流式处理和管道操作:Stream API支持流式处理的方式,利用管道将一系列的操作链接在一起,实现一种流程化的数据处理方式。这种方式更加直观和易于理解,适用于数据处理管道、数据流处理等场景。
🎄filter过滤数据 & foreach遍历数据
Stream -> filter & foreach函数的使用
public static void main(String[] args) {
// 创建一个ArrayList集合 并填充数据
ArrayList<String> list = new ArrayList<>();
list.add("张无忌"); list.add("周芷若"); list.add("赵敏");
list.add("张强"); list.add("张三丰");
// 1 获取流对象 Stream<String> stream = list.stream();
list.stream() // 当前list = ["张无忌","周芷若","赵敏","张强","张三丰"]
// 2 过滤数据 只留下以"张"开头的字符串
.filter(str -> str.startsWith("张")) // 当前list = ["张无忌","张强","张三丰"]
// 3 继续过滤数据 只留下list当中长度为2的字符串
.filter(str -> str.length() == 2) // 当前list = ["张强"]
// 4 遍历list中当前的所有字符串并进行打印
.forEach(str-> System.out.println(str));
/*打印结果(只有一行):
张强
*/
}
🎄Lambda表达式简化
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d","e");
// 获取到一条流水线 并把集合中的数据放到流水线上
Stream<String> stream1 = list.stream();
/**
* 使用终结方法打印一下流水线上的所有数据
*/
System.out.print("原始代码表达方式遍历:");
stream1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
//s:依次表示流水线上的每一个数据
System.out.print(s + " ");
}
});
/**
* Lambda简化表达方式
*/
System.out.print("\nLambda简化代码表达方式遍历:");
list.stream().forEach(s -> System.out.print(s + " "));
}
运行结果:
🎄双列集合HashMap使用流
public static void main(String[] args) {
// 1.创建双列集合
HashMap<String,Integer> hm = new HashMap<>();
// 2.添加数据
hm.put("aaa",111);
hm.put("bbb",222);
hm.put("ccc",333);
hm.put("ddd",444);
// 3.第一种获取stream流
System.out.println("hm.keySet().stream()获取流:");
hm.keySet().stream().forEach(s -> System.out.println(s));
// 4.第二种获取stream流
System.out.println("hm.entrySet().stream()获取流:");
hm.entrySet().stream().forEach(s-> System.out.println(s));
}
运行结果:
🎄数组使用流
public static void main(String[] args) {
//1.创建数组
int[] arr1 = {1,2,3,4,5,6,7,8,9,10};
String[] arr2 = {"a","b","c"};
//2.获取stream流
Arrays.stream(arr1).forEach(s-> System.out.print(s + " "));
System.out.println("\n============================");
Arrays.stream(arr2).forEach(s-> System.out.print(s + " "));
System.out.println("\n============================");
/**
* 【注意 Stream接口中静态方法of的细节】
* 方法的形参是一个可变参数 可以传递一堆零散的数据 也可以传递数组
* 但是数组必须是引用数据类型的 如果传递基本数据类型 是会把整个数组当做一个元素 放到Stream当中
* 比如下面的例子 因为arr1是基本数据类型的数组 所以只打印出了对象名
*/
Stream.of(arr1).forEach(s-> System.out.println(s));
}
运行结果:
🎄limit限制数量 && skip跳过元素
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
System.out.print("只输出前3个元素 -> ");
list.stream().limit(3).forEach(s -> System.out.print(s + " "));
System.out.print("\n跳过前4个元素 -> ");
list.stream().skip(4) .forEach(s -> System.out.print(s + " "));
System.out.print("\n先拿出前6个元素 再跳过3个元素 -> ");
list.stream().limit(6).skip(3).forEach(s -> System.out.print(s + " "));
System.out.print("\n先跳过3个元素 再拿出前3个元素 -> ");
list.stream().skip(3).limit(3).forEach(s -> System.out.print(s + " "));
}
运行结果:
🎄distinct去重 & concat合并
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "张无忌","张无忌","张无忌", "张三丰", "张三丰");
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2, "周芷若", "赵敏");
System.out.print("distinct去重 -> ");
list1.stream().distinct().forEach(s -> System.out.print(s + " "));
System.out.print("\n去重 + concat合并 -> ");
Stream.concat(list1.stream(),list2.stream()).distinct().forEach(s -> System.out.print(s + " "));
}
运行结果:
🎄map -> 转换(v) 流中的数据类型
通过使用map链式编程来转换流中的数据类型
修改Stream流中的数据 不会影响原来集合或者数组中的数据
/**
* 需求:只获取里面的年龄并进行打印 String->int
*/
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-15", "周芷若-14", "赵敏-13", "张强-20", "张三丰-100", "张翠山-40", "张良-35", "王二麻子-37", "谢广坤-41");
/**
* 当map方法执行完毕之后 流上的数据就变成了整数
* 所以在下面forEach当中 s依次表示流里面的每一个数据 这个数据现在就是整数了
*/
list.stream().map(new Function<String, Integer>() {
/**
* @param s 表示流里面的每一个数据
* @return 表示转换之后的数据
*/
@Override
public Integer apply(String s) {
String[] arr = s.split("-");
String ageString = arr[1];
int age = Integer.parseInt(ageString);
return age;
}
}).forEach(s-> System.out.print(s + " "));
System.out.println("\n------------------------");
/**
* 使用Lambda表达式 更简洁
*/
list.stream()
.map(s-> Integer.parseInt(s.split("-")[1]))
.forEach(s-> System.out.print(s + " "));
打印结果:
🎄count -> 统计数目
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山");
long count = list.stream().count();
System.out.println(count); // 6
🎄toArray() -> 收集流中的数据 放到数组中
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山");
Object[] a = list.stream().toArray();
System.out.println(Arrays.toString(a)); // [张无忌, 周芷若, 赵敏, 张强, 张三丰, 张翠山]
🎄collect -> 收集流中的数据放到 List Set Map
使用下面的数据来演示
collect
的使用
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "周芷若-女-14", "赵敏-女-13", "张强-男-20",
"张三丰-男-100", "张翠山-男-40", "张良-男-35", "王二麻子-男-37", "谢广坤-男-41");
把所有的男性收集到List集合当中
List<String> newList1 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());
把所有的男性收集到Set集合当中
Set<String> newList2 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
把所有的男性收集到Map集合当中 -> 需要指定键和值(原数据的那一部分作为键 那一部分作为值)
/**
* 完整使用模式
*/
Map<String, Integer> map = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
new Function<String, String>() {
@Override
public String apply(String s) {
//张无忌-男-15
return s.split("-")[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);
}
})
);
/**
* Lambda使用模式
*/
Map<String, Integer> map2 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(s -> s.split("-")[0], s -> Integer.parseInt(s.split("-")[2])));
/**
* 【打印结果】
* {张强=20, 张良=35, 张翠山=40, 王二麻子=37, 张三丰=100, 张无忌=15, 谢广坤=41}
*/
⚡使用顺序总结[Lambda]
filter -> [map] -> [limit|skip|distinct] -> [toArray()|count|collect]
⚡综合运用
注意下面的两种方式的收集结果的区别
(1)第一种使用map函数将两列数据封装到Actor类对应的字段 构建成Actor对象
(2)第二种使用collect(Collectors.toMap(..))
/*********************** 模拟数据准备 ***********************/
//1.创建两个ArrayList集合
ArrayList<String> manList = new ArrayList<>();
ArrayList<String> womenList = new ArrayList<>();
//2.添加数据
Collections.addAll(manList, "蔡坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖梁梁,27");
Collections.addAll(womenList, "赵小颖,35", "杨颖,36", "高元元,43", "张天天,31", "刘诗,35", "杨小幂,33");
//3.男演员只要名字为3个字的前两人
Stream<String> stream1 = manList.stream()
.filter(s -> s.split(",")[0].length() == 3)
.limit(2);
//4.女演员只要姓杨的,并且不要第一个
Stream<String> stream2 = womenList.stream()
.filter(s -> s.split(",")[0].startsWith("杨"))
.skip(1);
//5.把过滤后的男演员姓名和女演员姓名合并到一起
//演员信息封装成Actor对象。
/****** 把过滤后的男演员姓名和女演员姓名合并到一起 封装成Actor对象 *******/
// 【result -> 】 [Actor{name = 蔡坤坤, age = 24}, Actor{name = 叶齁咸, age = 23}, Actor{name = 杨小幂, age = 33}]
List<Actor> list = Stream.concat(stream1, stream2)
.map(s -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1])))
.collect(Collectors.toList());
/****** 使用 *******/
// 【result -> 】 {叶齁咸=23, 杨小幂=33, 蔡坤坤=24}
Map<String, Integer> map = Stream.concat(stream1, stream2)
.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1]));