不可变集合
- 创建不可变集合:数据不能修改,防御性拷贝到不可变集合中;被不可信库调用时,安全。
- 在List Set Map 接口中都存在静态方法of->获取不可变集合-->不能添加,删除,修改(只读)
List<String> list = List.of("","","");
//remove add set xxx都不可
Set<String> set = Set.of("","","");//无索引
//获取一个不可变set集合 参数保证唯一性
Map<String,String> map = Map.of("key","value");
//键不能重复 参数有上限(10个)
//参数多出10个时->ofEntries jdk10->copyOf
------------方一---------------
HashMap<String,String> map = new HashMap<>();
map.put("123","123");//put超过十个
//获取所有键值对象 Entry对象
Set<Map.Entry<String,String>>entries = map.entrySet();
//把entries变成一个数组 类型时entry对象
//toArray方法会比较集合的长度与数组的长度
//集合长度>数组长度 根据实际集合数据个数 新建数组,反之<= 直接用 多出来的为nul
Map.Entry[] arr = entires.toArray(new Map.Entry[0]);
Map map1 = Map.ofEntries(arr);
//不可变创完
---------------方二------------------
//简化
Map<Object,Object> map1 = Map.ofEntries(map.entrySet().toArray(new Map.Entry[0]));
---------------方三------------------
//jdk10
Map<String,String> map1 = mapMap.copyOf(map);
Stream
简介
结合Lambda表达式 简化集合、数组的操作
特性:
Stream 自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。
流式处理
eg:
比如希望对一个包含整数的集合中筛选出所有的偶数,并将其封装成为一个新的List返回
List<Integer> evens = new ArrayList<>();
for (final Integer num : nums) {
if (num % 2 == 0) {
evens.add(num);
}
}
通过java8的流式处理,将代码简化为:
List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
------eg2--------
ArrayList<String> list = new ArrayList<>();
list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3 ).forEach(name -> System.out.println(name));
解释:stream()
操作将集合转换成一个流,filter()
执行我们自定义的筛选处理,这里是通过lambda表达式筛选出所有偶数,最后我们通过collect()
对结果进行封装处理,并通过Collectors.toList()
指定其封装成为一个List集合返回。
一个流式处理可以分为三个部分:转换成流、中间操作、终端操作
以集合为例,一个流式处理的操作我们首先需要调用
stream()
函数将其转换成流,然后再调用相应的中间操作
达到我们需要对集合进行的操作,比如筛选、转换等,最后通过终端操作
对前面的结果进行封装,返回我们需要的形式。
步骤:
-
得到stream流 放数据
获取方式 方法名 说明 单列集合 default Stream<E> stream() Collection中的默认方法 双列集合 无(勇keySet or entrySet 先变成单列) 无法直接使用stream流 数组 public static <T> Stream <T> stream (T[] array) Arrays工具类中的静态方法 一堆零散数据 public static <T> Stream <T> of (T...values) Stream接口中的静态方法 单列集合:
ArrayList<String> list = new ArrayList<>(); Collections.addAll(list,"a","b","c","d","e","f"); //获取一条流水线 将集合数据放流水线上 /*Stream<String> stream = list.stream(); stream.forEach(new Consumer<String>() { @Override public void accept(String s) { // s 一次表示流水线上的每一个数据 System.out.println(s); } });*/ list.stream().forEach(s-> System.out.println(s));
双列集合:
//创建双列集合 HashMap<String, Integer> hm = new HashMap<>(); hm.put("aaa",1111); hm.put("bbb",2222); hm.put("ccc",3333); hm.put("mmm",4444); //获取stream流 //打印所有键 hm.keySet().stream().forEach(s-> System.out.println(s)); //打印所有键值对 hm.entrySet().stream().forEach(s-> System.out.println(s));
数组:
//创建数组 int[] arr = {1,2,3,4,5,6,7}; Arrays.stream(arr).forEach(s-> System.out.println(s));
零散数据:
Stream.of(1,2,3,4).forEach(s-> System.out.println(s));
注意点:
引用数据类型数组可以Stream.of 但是基本数据类型不行
-
利用流中API操作
过滤 转换 -> 中间方法 (之后可继续调用其他方法)
统计 打印 -> 终结方法 (之后不可调用其他)
中间操作
筛选与切片
filter ->过滤
limit -> 获取前几个元素
skip -> 跳过前几个元素
distinct -> 元素去重 依赖(hashCode和equals方法)
concat -> 合并a,b两个流为一个流(父类)
filter
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a123","b123","c222","d11","e22","f11");
//过滤
// list.stream().filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// //返回值为true 留下 false 舍去
// return s.startsWith("a");
// }
// }).forEach(s-> System.out.println(s));
list.stream().filter(s->s.startsWith("a")).forEach(s-> System.out.println(s));
limit/skip
//得到前三个
list.stream().limit(3).forEach(s-> System.out.println(s));
//跳过前4个
list.stream().skip(4).forEach(s-> System.out.println(s));
distinct
list.stream().distinct().forEach(s-> System.out.println(s));
concat
Stream.concat(list.stream(),list2.stream()).forEach(s-> System.out.println(s));
映射
map -> 转换流中数据类型 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap -> 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
mapToDouble -> 产生一个新的 DoubleStream
mapToInt -> 产生一个新的 IntStream
mapToLong -> 产生一个新的 LongStream。
注意:不影响原数据
map
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a123-15","b123-56","a123-22","d11-25","e22-525","f11-59");
//只获取里面 -后数据 String ->int
// 第一个类型:流中数据原本类型 第二个类型:要转的类型
//apply形参s:流中数据
//返回值 转换后数据
//当map方法执行完毕后 流上的数据 变成了整数
// list.stream().map(new Function<String, Integer>() {
// @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.println(s));
list.stream().map(s-> Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a123-男-15","b123-男-56","a123-女-22","d11-女-25","e22-女-525","f11-女-59");
//拿到名字长度大于3的名字
//先拿到名字
List<String> collect = list.stream().map(e -> e.split("-")[0]).filter(s -> s.length() > 3).collect(Collectors.toList());
List<String> collect = list.stream().map(Employee::getName).filter(s -> s.length() > 3).collect(Collectors.toList());
//假设希望筛选出所有专业为计算机科学的学生姓名,可以在filter筛选的基础之上,通过map将学生实体映射成为学生姓名字符串
List<String> names = students.stream()
.filter(student -> "计算机科学".equals(student.getMajor()))
.map(Student::getName).collect(Collectors.toList());
//希望计算所有专业为计算机科学学生的年龄之和
int totalAge = students.stream()
.filter(student -> "计算机科学".equals(student.getMajor()))
.mapToInt(Student::getAge).sum();
flatMap
flatMap是将一个流中的每个值都转成一个个流,然后再将这些流扁平化成为一个流 stream中stream
//输出构成这一数组的所有非重复字符
String[] strs = {"java8", "is", "easy", "to", "use"};
List<String[]> distinctStrs = Arrays.stream(strs)
.map(str -> str.split("")) // 映射成为Stream<String[]>
.distinct()
.collect(Collectors.toList());
/*
[j, a, v, a, 8]
[i, s]
[e, a, s, y]
[t, o]
[u, s, e]
*/
List<String> distinctStrs = Arrays.stream(strs)
.map(str -> str.split("")) // 映射成为Stream<String[]>
.flatMap(Arrays::stream) // 扁平化为Stream<String>
.distinct()
.collect(Collectors.toList());
//books->list
Book book = writers.stream().flatMap(writer -> writer.getBooks().stream())
.max(new BookComparator()).get();
并、交、差
List<String> A = new ArrayList<>();
Collections.addAll(A,"1","2","3","4");
List<String> B = new ArrayList<>();
Collections.addAll(A,"3","4","5","6","7");
//并集
A.addAll(B);
List<String> AuB = A.stream().distinct().collect(Collectors.toList());
System.out.println("并集:" + AuB);
//[1, 2, 3, 4, 5, 6, 7]
//交集 B::contains = s -> B.contains(s) 高版本IDEA会提示转换
List<String> AnB = A.stream().filter(B::contains).collect(Collectors.toList());
System.out.println("交集:" + AnB);
//[3, 4]
//差集
List<String> difference = A.stream().filter(s -> !B.contains(s)).collect(Collectors.toList());
System.out.println("A中B的补集:" + difference);
排序
sorted() -> 自然排序 升序
sorted(Comparator com) -> 按比较器顺序排序
//升序
//1.自然排序
list = list.stream().sorted().collect(Collectors.toList());
//2.比较器 根据年龄排序
list = list.stream().sorted(Comparator.comparing(Student::getAge)).collect(Collectors.toList());
//降序
//1.自然排序 使用Comparator 提供的reverseOrder() 方法
list = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
//2.比较器 根据年龄排序
list = list.stream().sorted(Comparator.comparing(Student::getAge).reversed()).collect(Collectors.toList());
//多字段排序
//先按姓名升序,姓名相同则按年龄升序
list = list.stream().sorted(Comparator.comparing(Student::getName).thenComparing(Student::getAge)).collect(Collectors.toList());
Match 匹配
match用来做匹配操作,它的返回值是一个 boolean
类型。通过 match
, 可以方便的验证一个 list
中是否存在某个类型的元素。
// 验证 list 中 string 是否有以 a 开头的, 匹配到第一个,即返回 true
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
// 验证 list 中 string 是否都是以 a 开头的
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
// 验证 list 中 string 是否都不是以 z 开头的,
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
终结方法
forEach -> 遍历
(long)count -> 统计
Reduce -> 规约
toArray() -> 收集流中的数据 放到数组中
collect (Collector collector) -> 收集流中数据 放到集合
Count
count
是一个终端操作,它能够统计 stream
流中的元素总数,返回值是 long
类型
// 先对 list 中字符串开头为 b 进行过滤,让后统计数量
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); // 3
Reduce
Reduce
中文翻译为:减少、缩小。通过入参的 Function
,能够将 list
归约成一个值。它的返回类型是 Optional
类型。
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
BigDecimal subjectTotalIncomeBudget = entry.getValue().stream()
.map(PmJtProjectSaleContractPrice::getTotalAmountExcludeTax).reduce(BigDecimal.ZERO,BigDecimal::add);
//BigDecimal.ZERO: 这表示初始值。在执行 reduce 操作时,首次使用的值为初始值。在这里,BigDecimal.ZERO 表示初始值为 0。
//BigDecimal::add: 这表示一个 BinaryOperator,即表示如何将两个元素结合起来。在这里,使用了 BigDecimal 类的 add 方法,表示将两个 BigDecimal 相加。
collect
//toArray()
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a123-15","b123-56","a123-22","d11-25","e22-525","f11-59");
System.out.println(Arrays.toString(list.stream().toArray()));
// String[] arr = list.stream().toArray(new IntFunction<String[]>() {
// @Override
// public String[] apply(int value) {
// return new String[value];
// }
// });
System.out.println(Arrays.toString(list.stream().toArray(value->new String[value])));
collect (Collector collector) -> 收集流中数据 放到集合中 (List Set Map)
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a123-男-15","b123-男-56","a123-女-22","d11-女-25","e22-女-525","f11-女-59");
//收集男性
list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
//.collect(Collectors.toList());收集到List集合中 不去重
//.collect(Collectors.toSet());收集到Set中 去重
-----------------------------------------------------------
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a123-男-15","b123-男-56","a123-女-22","d11-女-25","e22-女-525","f11-女-59");
//收集到map中 键值
//注意点:键不能重复
//收集男性 键:姓名 值:年龄
/*list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
*//* toMap: 键的规则 值的规则
键的规则:Function 泛型 (流中每一个数据的类型,map集合中键的类型) ->生成键 返回键
值的规则:Function 泛型 (流中每一个数据的类型,map集合中值的类型) ->生成值 返回值
*//*
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
//生成键
return s.split("-")[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);
}
}));*/
list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(s->s.split("-")[0],s-> Integer.parseInt(s.split("-")[2])));
例子
//List<List<FieldAttributeDto>> tableValue 根据FieldAttributeDto中的FiledName="records_projectStage"的value的值(string)来进行排序(升序)
//flatMap 当阶段1-11时出错
List<FieldAttributeDto> flattenedSortedTableValue = tableValue.stream()
.flatMap(List::stream) // 将嵌套的列表扁平化为单个元素的流
.filter(dto -> "records_projectStage".equals(dto.getFieldName())) // 过滤出符合条件的 FieldAttributeDto
.sorted(Comparator.comparing(FieldAttributeDto::getLabelValue)) // 对符合条件的 FieldAttributeDto 进行排序
.collect(Collectors.toList()); // 收集结果为列表
//解决 限制 "阶段"开头
List<FieldAttributeDto> flattenedSortedTableValue = tableValue.stream()
.flatMap(List::stream) // 将嵌套的列表扁平化为单个元素的流
.filter(dto -> "records_projectStage".equals(dto.getFieldName())) // 过滤出符合条件的 FieldAttributeDto
.sorted(Comparator.comparing(this::parseStage))
.collect(Collectors.toList()); // 收集结果为列表
// 自定义方法,将阶段字符串解析为整数
private int parseStage(FieldAttributeDto dto) {
String stage = dto.getLabelValue();
if (stage.startsWith("阶段")) {
return Integer.parseInt(stage.substring(2));
}
return 0; // 默认值
}
List<List<FieldAttributeDto>> sortedTableValue = tableValue.stream()
.map(list -> list.stream()
.filter(dto -> dto.getFieldName().equals("records_projectStage"))
.findFirst()
.orElse(null))
.filter(dto -> dto != null)
.sorted(Comparator.comparing(FieldAttributeDto::getValue))
.map(dto -> tableValue.stream()
.filter(list -> list.contains(dto))
.findFirst()
.orElse(null))
.filter(list -> list != null)
.collect(Collectors.toList());