Stream流式编程
不可变集合
- 什么是不可变集合
是一个长度不可变,内容也无法修改的集合
- 使用场景
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
当集合对象被不可信的库调用时,不可变形式是安全的。
如:斗地主的54张牌,是不能添加,不能删除,不能修改的
- 不可变集合分类
-
不可变的list集合
public static void main(String[] args) { //创建不可变的List集合, "张三", "李四", "王二", "麻子" //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作 List<String> list = List.of("张三", "李四", "王二", "麻子"); }
-
不可变的set集合
public static void main(String[] args) { //创建不可变的Set集合,"张三", "李四", "王二", "麻子" //注:当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性 //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作 Set<String> set = Set.of("张三","张三", "李四", "王二", "麻子"
-
不可变的map集合
键值对个数小于等于10
public static void main(String[] args) {
/*
创建Map的不可变集合
细节1:
键是不能重复的
细节2:
Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对
细节3:
如果我们要传递多个键值对对象,数量大于10个,在Map接口中还有一个方法
*/
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
Map<String, String> map = Map.of("张三", "南京", "张三", "北京", "王五", "上海",
"赵六", "广州", "孙七", "深圳", "周八", "杭州",
"吴九", "宁波", "郑十", "苏州", "刘一", "无锡",
"陈二", "嘉兴");
Set<String> keys = map.keySet();
for (String key : keys) {
String value = map.get(key);
System.out.println(key + "=" + value);
}
System.out.println("--------------------------");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
System.out.println("--------------------------");
}
键值对个数大于10
public static void main(String[] args) {
/*
创建Map的不可变集合,键值对的数量超过10个
*/
//1.创建一个普通的Map集合
HashMap<String, String> hm = new HashMap<>();
hm.put("张三", "南京");
hm.put("李四", "北京");
hm.put("王五", "上海");
hm.put("赵六", "北京");
hm.put("孙七", "深圳");
hm.put("周八", "杭州");
hm.put("吴九", "宁波");
hm.put("郑十", "苏州");
hm.put("刘一", "无锡");
hm.put("陈二", "嘉兴");
hm.put("aaa", "111");
//2.利用上面的数据来获取一个不可变的集合
/*
//获取到所有的键值对对象(Entry对象)
Set<Map.Entry<String, String>> entries = hm.entrySet();
//把entries变成一个数组
Map.Entry[] arr1 = new Map.Entry[0];
//toArray方法在底层会比较集合的长度跟数组的长度两者的大小
//如果集合的长度 > 数组的长度 :数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
//如果集合的长度 <= 数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
Map.Entry[] arr2 = entries.toArray(arr1);
//不可变的map集合
Map map = Map.ofEntries(arr2);
map.put("bbb","222");*/
//Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
Map<String, String> map = Map.copyOf(hm);
map.put("bbb","222");
}
Stream流
Stream流的优点
- 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤数据、逐一打印
- Stream流把真正的函数式编程风格引入到Java中
- 代码简洁
Stream流的常见生成方式
Stream流的三类方法
-
获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
-
中间方法
- 流水线上的操作
- 一次操作完毕之后,还可以继续进行其他操作
-
终结方法
-
一个Stream流只能有一个终结方法
-
是流水线上的最后一个操作
-
生成Stream流的方式
-
Collection体系集合
使用默认方法stream()生成流, default Stream stream()
public class CollectionStreamDemo { public static void main(String[] args) { //Collection的集合可以使用默认方法stream()生成流 //List生成流 List<String> list = new ArrayList<>(); Stream<String> listStream = list.stream(); //Set生成流 Set<String> set = new HashSet<>(); Stream<String> setStream = set.stream(); } }
-
Map体系集合
把Map转成Set集合,间接的生成流
public class MapStreamDemo { public static void main(String[] args) { //Map体系的集合间接的生成流 Map<String,String> map = new HashMap<>(); Stream<String> keyStream = map.keySet().stream(); Stream<String> valuesStream = map.values().stream(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); } }
-
数组
通过Arrays中的静态方法stream生成流
public class ArrayStreamDemo { public static void main(String[] args) { //数组可以通过Arrays中的静态方法stream生成流 int[] array1 = {1,2,3,4,5,6}; IntStream intStream = Arrays.stream(array1); String[] array2 = {"a","b","c","d"}; Stream<String> stringStream = Arrays.stream(array2); } }
-
同种数据类型的多个数据
通过Stream接口的静态方法of(T… values)生成流
public class OtherStreamDemo {
public static void main(String[] args) {
//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流
Stream<String> Stream1 = Stream.of("hello", "world", "java");
Stream<Integer> Stream2 = Stream.of(10, 20, 30);
}
}
Stream流中间操作方法
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作
- 常见方法
方法名 | 说明 |
---|---|
Stream filter(Predicate predicate) | 用于对流中的数据进行过滤(过滤) |
Stream limit(long maxSize) | 返回此流中的元素组成的流,截取前指定参数个数的数据(截取) |
Stream skip(long n) | 跳过指定参数个数的数据,返回由该流的剩余元素组成的流(跳过) |
static Stream concat(Stream a, Stream b) | 合并a和b两个流为一个流(合流) |
Stream distinct() | 返回由该流的不同元素(根据Object.equals(Object) )组成的流(去重) |
- filter代码演示
public class StreamFilterDemo {
public static void main(String[] args) {
//创建集合list
List<String> list = new ArrayList<>();
//往集合添加数据
Collections.addAll(list,"张三","李四","王二","麻子","李太白","张三丰","李白");
//过滤姓李的
list.stream().filter(new Predicate<String>() {
@Override // s表示流中的每一个数据
public boolean test(String s) {
// 若为true,当前数据保留,若为false当前数据不予保留
boolean result = s.startsWith("李");
return result;
} // 遍历过滤后的数据 得到结果 李四 李太白 李白
}).forEach(s -> System.out.println(s));
//因为Predicate接口中只有一个抽象方法test,所以我们可以使用lambda表达式来简化
System.out.println("========================================");
//lambda表达式如下
list.stream().filter(s -> s.startsWith("李")).forEach(s -> System.out.println(s));
}
}
- limit&skip代码演示
public class StreamLimitAndSkipDemo {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
//在集合中批量存储数据
Collections.addAll(list,"张三","李四","王二","麻子","李太白","张三丰","李白");
//limit(2)截取集合中前两个数据 输出结果为: 张三 李四
list.stream().limit(2).forEach(s -> System.out.println(s));
System.out.println("===================================");
//skip(4)跳过前四个数据输出 输出结果为: 李太白 张三丰 李白
list.stream().skip(4).forEach(s -> System.out.println(s));
System.out.println("===================================");
//截取前5个且在此结果集中跳过前三个 输出结果为: 麻子 李太白
list.stream().limit(5).skip(3).forEach(s -> System.out.println(s));
}
}
- concat&distinct代码演示
public class StreamConcatAndDistinctDemo {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
//在集合中批量存储数据
Collections.addAll(list, "张三", "李四", "王二", "麻子", "李太白", "张三丰", "李白");
//截取集合前四个元素返回新的流limitStream
Stream<String> limitStream = list.stream().limit(4);
//跳过集合前两个元素返回新的流skipStream
Stream<String> skipStream = list.stream().skip(2);
//合并流做遍历操作,遍历结果:张三 李四 王二 麻子 王二 麻子 李太白 张三丰 李白
//流只能使用一次,下方需要测试distinct(去重),因此注释掉这行代码
//Stream.concat(limitStream, skipStream).forEach(s -> System.out.println(s));
//合并流去重后做遍历操作,遍历结果:张三 李四 王二 麻子 李太白 张三丰 李白
Stream.concat(limitStream,skipStream).distinct().forEach(s -> System.out.println(s));
}
}
Stream流终结操作方法
终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
- 常见方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行操作 |
long count() | 返回此流中的元素数 |
- 代码演示
public class StreamForEachAndCountDemo {
public static void main(String[] args) {
//创建集合
ArrayList<String> list = new ArrayList<>();
//在集合中批量存储数据
Collections.addAll(list,"张三","李四","王二","麻子","李太白","张三丰","李白");
//调用count()返回集合中元素个数
long count = list.stream().count();
//输出count值 输出结果为:7
System.out.println(count);
System.out.println("=========================");
//在forEach方法的底层,会循环获取到流中的每一个数据.
//并循环调用accept方法,并把每一个数据传递给accept方法
//s就依次表示了流中的每一个数据.
//所以,我们只要在accept方法中,写上处理的业务逻辑就可以了.
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("=========================");
//因为Consumer接口中,只有一个accept方法,可以使用lambda表达式的简化格式
list.stream().forEach(s -> System.out.println(s));
}
}
Stream流的收集操作
对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中
- 常用方法
方法名 | 说明 |
---|---|
R collect(Collector collector) | 把结果收集到集合中 |
- 工具类Collectors提供了具体的收集方式
方法名 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |
- 代码演示
public class StreamCollectDemo {
public static void main(String[] args) {
// toList和toSet方法演示
ArrayList<Integer> list1 = new ArrayList<>();
for (int i =1;i<=10;i++){
list1.add(i);
}
Collections.addAll(list1,10,10,10,10);
//filter负责过滤数据的.
//collect负责收集数据.
//获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
//Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.
List<Integer> collect = list1.stream().filter(s->s%2==0).collect(Collectors.toList());
System.out.println(collect); //[2, 4, 6, 8, 10, 10, 10, 10, 10]
System.out.println("==============================");
Set<Integer> collect1 = list1.stream().filter(s->s%2==0).collect(Collectors.toSet());
System.out.println(collect1); //[2, 4, 6, 8, 10]
// toMap方法演示
ArrayList<String> list2 = new ArrayList<>();
list2.add("zhangsan,23");
list2.add("lisi,24");
list2.add("wangwu,25");
System.out.println("==============================");
//保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
Map<String, Integer> map = list2.stream()
//过滤年龄大于等于24岁的人
.filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
.collect(Collectors.toMap(
//第一个lambda表达式就是如何获取到Map中的键
//第二个lambda表达式就是如何获取Map中的值
s -> s.split(",")[0],
s -> Integer.parseInt(s.split(",")[1])));
System.out.println(map);//{lisi=24, wangwu=25}
}
}