Lambda
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
1、 过滤 filter()
这里是筛选得到符合常量"name"的数据
//如果是实体类类型
List<Person> personList = new ArrayList<Person>();
personList.stream().filter(m->m.getName.equalsIgnoreCase("name")).collect(Collectors.toList());
//如果是map类型
List<Map<String,Object> mapList = new ArrayList<>();
mapList.stream().filter(m->m.get("name").toString.equalsIgnoreCase("name")).collect(Collectors.toList())
.stream()
:
将集合转换为流
.equalsIgnoreCase
:
用于比较两个字符串是否相等,忽略大小写
.collect(Collectors.toList())
:
将流(Stream)中的元素收集到一个列表(List)中的方法
2、limit限制获取个数
List<Person> personList = new ArrayList<Person>();
personList.stream().limit(5).collect(Collectors.toList())
3、groupingBy
用法
一个参数
第一个参数:按照该参数规则进行分组,规则的结果为整个结果Map的key。
结果形态:Map<String,List>,Object:根据实际情况显示。结果返回一个Map集合,Map的key是按照该规则执行后返回的每个结果,Map的value是一个List集合,该集合中的值是能满足他能得到对应key的参数规则,但是还未被处理过的对象。
例子:按照名称进行分类
//实体类类型
List<Type> list = baseMapper.getList();
//map类型
List<Map<String,Object>> mapList = baseMapper.getMapList();
Map<String, List<Type>> map1 = list.stream().collect(groupingBy(Type::getName));
Map<String, List<Map<String,Object>>> map2 = mapList.stream().collect(groupingBy(m->m.get("name").toString()));
结果name为map的key,List为map的值。
两个参数
第一个参数:按照该参数规则进行分组,规则的结果为整个结果Map的key。
第二个参数:是Collector类型,以上面“有一个参数的groupBy”的value为基础,对value进行再次处理,并将结果对象更新成Map的value。
结果形态:Map<String,Object>,Object:根据实际情况显示,结果返回一个Map集合,Map的key是该按照第一个参数的规则执行后返回的每个结果的字符串,Map的value是再次被处理后的的对象。
例子:
Map<String, Long> map1 = list.stream()
.collect(Collectors.groupingBy(Type::getName, Collectors.counting()))
Map<String, Long> map2 = mapList.stream()
.collect(Collectors.groupingBy(m->m.get("name").toString(), Collectors.counting()))
Map<Object, Map<String, Long>>orginalList.stream()
.collect(Collectors.groupingBy(
map -> ((LocalDateTime) map.get("createTime")).toLocalDate(),
Collectors.groupingBy(
map -> map.get("levels").toString(),
Collectors.counting()
)
))
Collectors.counting()
:
用于计数,并返回一个Long类型的结果
结果name为map的key,分组name的个数为map的值({name=2, name2=1})。
三个参数
第一个参数:按照该参数规则进行分组,规则的结果为整个结果Map的key。
第二个参数:添加了对结果Map的生成方式,默认是HashMap(即我们要指定返回Map的具体类型,TreeMap,LinkedMap等)
第三个参数:是Collector类型,以上面“有一个参数的groupby”的value为基础,对value进行再次处理,并将结果对象更新成Map的value。
例子:
final Map<String, Map<String, List<Type>>> result3 = list.stream()
.collect(Collectors.groupingBy(Type::getName, LinkedHashMap::new, Collectors.groupingBy(Type::getAge)));
4、遍历
map.entrySet()获取全部的键值对。 Key和Value存储到一个Set集合中Set<Map.Entry<K,V>>,通过entry分别获取key和value
HashMap<String, Integer> map = new HashMap<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.print("key = " + entry.getKey());
System.out.println(", value = " + entry.getValue());
}
.foreach()
和.entrySet()
是Java中用于迭代集合的两个不同的方法。
.foreach()
是Iterable
接口的一个默认方法,可以用于对集合中的每个元素执行指定的操作。它接受一个lambda表达式或方法引用作为参数,并将该操作应用于集合中的每个元素。.foreach()
方法没有返回值,只是对集合进行遍历和操作。
以下是使用.foreach()
方法遍历一个列表并打印每个元素的示例:
javaCopy CodeList<String> list = Arrays.asList("apple", "banana", "orange");
list.forEach(item -> System.out.println(item));
.entrySet()
是Map
接口的一个方法,用于获取包含键值对的Set
视图。每个键值对都表示为一个Map.Entry
对象,其中包括键和对应的值。我们可以使用.entrySet()
方法来遍历并操作Map
中的键值对。
以下是使用.entrySet()
方法遍历一个Map
并打印键值对的示例:
javaCopy CodeMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
在这个例子中,我们使用map.entrySet()
方法获取包含键值对的Set
视图,并通过增强的for
循环遍历每个键值对。通过entry.getKey()
和entry.getValue()
方法,我们可以获取键和对应的值。
综上所述,.foreach()
方法用于遍历集合中的元素,而.entrySet()
方法用于遍历和操作Map
中的键值对。
5、排序
只使用排序的时候可以不用显示的调用.stream()
可以直接在集合上调用.sort()
方法
//map排序
List<Map<String,Object>> orginalList = new ArryList<>();
List<Map<String, Object>> result = orginalList.stream()
//通过assetId分组并计数,得到map类型
.collect(Collectors.groupingBy(m -> m.get("assetId"), Collectors.counting()))
//从大到小进行排序
.entrySet().stream()
.sorted(Map.Entry.<Object, Long>comparingByValue().reversed())
.limit(5)
//另一种
mapList.sort(Comparator.comparing(map -> ((LocalDate) map.get("createTime"))));
//List排序
List<Person> personList = new ArrayList<Person>();
//根据薪资升序排列
personList.stream()
.sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.collect( Collectors.toList() );
//根据年龄升序排列
personList.stream()
.sorted(Comparator.comparing(Person::getAge))
.collect(Collectors.toList());
//降序排序
personList.stream()
.sorted(Comparator.comparing(Person::getAge).reversed())
comparingByKey()
根据键排序,comparingByValue()
根据值排序
Comparator.comparing()
:
进行比较的比较器
.reversed()
:
降序排序(升序不加即可)
6、stream().map().collect(Collectors.toList())
把list对象里面的一个列生成对应的一个新list集合
该方法允许我们使用一个函数来将流中的每个元素映射到另一个元素上。它返回一个新的流
//实体类类型
List<Person> persons = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35)
);
List<String> names = persons.stream()
.map(Person::getName)
.collect(Collectors.toList());
//map类型
List<Map<String, Object>> people = Arrays.asList(
Map.of("name", "Alice", "age", 25),
Map.of("name", "Bob", "age", 30),
Map.of("name", "Charlie", "age", 35)
);
List<String> names = people.stream()
.map(person -> (String) person.get("name"))
.collect(Collectors.toList());
这里是代表取出每个每项中的name项
复杂一点的看下面
List<Map<String,Object>> orginalList = baseMapper.getList();
List<Map<String, Object>> result = orginalList.stream()
//通过assetId分组并计数,变成map类型
.collect(Collectors.groupingBy(m -> m.get("assetId"), Collectors.counting()))
//从大到小进行排序
.entrySet().stream()
.sorted(Map.Entry.<Object, Long>comparingByValue().reversed())
//取前五个
.limit(5)
//组成新列表
.map(entry -> {
Map<String, Object> map = new HashMap<>();
map.put("assetId", entry.getKey());
map.put("thecount", entry.getValue());
//获取第一个匹配的
Optional<Map<String, Object>> asset = orginalList.stream()
.filter(m -> m.get("assetId").equals(entry.getKey()))
.findFirst();
//检查 Optional 对象是否包含值,有就存进去
asset.ifPresent(map::putAll);
return map;
})
.collect(Collectors.toList());
.findFirst()
:
取出第一个符合要求的
.ifPresent
:
是否包含值
map::putAll
是 Java 8 中 Optional
类的 .ifPresent()
方法结合 Map
的 .putAll()
方法使用的一种常见写法。Optional
是一个用来处理可能为null的值的容器类。.ifPresent()
方法接受一个 Consumer
函数作为参数,并在 Optional
对象包含非空值时执行该函数。而 Map
的 .putAll()
方法用于将另一个 Map
中的所有映射关系复制到当前 Map
中。
7、相同list去重
去重一般使用.distinct()
方法但是该方法并不能设置条件
List<Map<String, Object>> list = new ArrayList<>();
// 使用流操作对 List<Map> 去重
List<Map<String, Object>> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
如果想要对去重设置条件,就需要设置一个过滤器
//设置过滤器 使用 ConcurrentHashMap 来实现对对象集合的去重。通过调用 putIfAbsent() 方法,可以保证在多线程环境下,只有一个线程能够成功添加到哈希表中,避免重复添加
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return object -> seen.putIfAbsent(keyExtractor.apply(object), Boolean.TRUE) == null;
//通过名称去重
List<Person> distinctUsers =personList.stream()
.distinct(distinctByKey(Person::getName))
.collect(Collectors.toList())
Predicate
接口表示一个断言(即一个返回布尔值的函数),用于对给定的输入进行条件判断。
ConcurrentHashMap
是 Java 中的一个线程安全的哈希表实现,它允许多个线程同时对其进行读取和写入操作,而不需要显式的同步。
putIfAbsent(key, value)
是 ConcurrentHashMap
提供的一个原子性方法。它会将指定的 key
和 value
添加到哈希表中,但只有在该 key
不存在时才生效。如果 key
已经存在,则返回已存在的值,不会进行替换。
keyExtractor.apply(object)
表示对 object
应用 keyExtractor
函数,以提取关键属性值作为哈希表的键。
seen.putIfAbsent(keyExtractor.apply(object), Boolean.TRUE) == null
的含义是:如果 keyExtractor.apply(object)
作为键的条目尚不存在于 seen
哈希表中,那么将其添加,并返回 null
;否则,如果已存在,则返回哈希表中相应键的当前值。
8、stream().collect(Collectors.toMap)
stream().collect(Collectors.toMap)
是 Java 8 中 Stream API 提供的一个用于将流中的元素收集到 Map 集合中的方法。
它的用法如下:
javaCopy CodeMap<KeyType, ValueType> map = stream.collect(Collectors.toMap(keyMapper, valueMapper));
keyMapper
和 valueMapper
是两个函数(或 Lambda 表达式),用于从流中的元素中提取键和值。
keyMapper
函数将流中的元素映射为键,valueMapper
函数将流中的元素映射为值。这两个函数应该返回对应的键类型和值类型。
collect(Collectors.toMap())
方法会根据提供的键和值的映射关系,将流中的元素收集到一个新的 Map
集合中。
需要注意的是,如果流中包含重复的键,则 toMap()
方法会抛出 IllegalStateException
异常。为了避免这种情况,我们可以使用第三个参数 mergeFunction
,来指定当存在重复键时如何处理。
下面是一个简单的示例代码,演示了如何使用 stream().collect(Collectors.toMap)
方法:
javaCopy Codeimport java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// 创建一个流
Stream<String> stream = Stream.of("apple", "banana", "orange");
// 将流中的元素收集到 Map 集合中,键为元素的长度,值为元素本身
Map<Integer, String> map = stream.collect(
Collectors.toMap(String::length, Function.identity()));
// 打印结果
System.out.println(map); // 输出:{5=apple, 6=orange, 6=banana}
}
}
在这个示例中,用 toMap()
方法将流中的元素收集到一个新的 Map
集合中,其中键是字符串的长度,值是字符串本身。
由于流中存在两个长度为 6 的元素(“banana” 和 “orange”),因此会抛出 IllegalStateException
异常。为了解决这个问题,我们可以使用上述提到的第三个参数 mergeFunction
,来指定如何处理重复键的情况。
Collectors.toMap
和 Collectors.groupingBy
都是 Java 8 Stream API 中的收集器(Collector),用于对流进行收集和分组操作。它们的主要异同点如下:
相同点:
- 都用于收集流的元素:无论是
toMap
还是groupingBy
,都用于对流中的元素进行收集,但采取了不同的收集策略。 - 都返回特定类型的结果:这两个收集器最终都返回特定类型的结果,例如
toMap
返回一个Map类型,而groupingBy
返回一个Map类型,其中值是根据分类函数分组的元素。
不同点:
- 收集策略不同:
toMap
收集器用于将流中的元素映射为键值对,并且可以通过合并函数处理重复键的情况(也就是说他不会保留重复值有一个去重的作用,默认是保留新出现的);而groupingBy
收集器则是根据分类函数将流中的元素分组(重复的数据会在value中以数组的形式存在)。 - 适用场景不同:
toMap
更适合将流中的元素转换为一个Map,例如将对象的某个属性作为键,对象本身作为值;而groupingBy
则更适合按照某个属性对元素进行分组。
总的来说,toMap
用于将流中的元素转换为一个Map,而 groupingBy
则用于按照某个属性对元素进行分组。在实际应用中,根据具体情况选择合适的收集器能够更好地完成对流的处理和整理。
9、match方法
(1)allMatch()
匹配到全部元素和指定的元素相等,返回true
allMatch()方法接受一个Predicate(断言)作为参数,并返回一个布尔值。当流中的所有元素都满足给定的条件时,allMatch()方法返回true;否则,返回false。
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 检查是否所有元素都大于0
boolean allGreaterThanZero = numbers.stream().allMatch(n -> n > 0);
System.out.println(allGreaterThanZero); // 输出: true
// 检查是否所有元素都为偶数
boolean allEvenNumbers = numbers.stream().allMatch(n -> n % 2 == 0);
System.out.println(allEvenNumbers); // 输出: false
}
}
(2)anyMatch()
匹配到任何一个元素和指定的元素相等,返回true
验证流中的所有元素是否满足某个条件时非常有用。它可以用于筛选、验证和处理流中的元素。anyMatch()方法接受一个Predicate(断言)作为参数,并返回一个布尔值。当流中至少有一个元素满足给定的条件时,anyMatch()方法返回true;否则,返回false。
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 检查是否存在大于3的元素
boolean anyGreaterThanThree = numbers.stream().anyMatch(n -> n > 3);
System.out.println(anyGreaterThanThree); // 输出: true
// 检查是否存在负数元素
boolean anyNegativeNumber = numbers.stream().anyMatch(n -> n < 0);
System.out.println(anyNegativeNumber); // 输出: false
}
}
(3)noneMatch
匹配到全部元素和指定的元素都不相等,返回true
noneMatch()方法接受一个Predicate(断言)作为参数,并返回一个布尔值。当流中没有任何一个元素满足给定的条件时,noneMatch()方法返回true;否则,返回false。
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 检查是否没有大于5的元素
boolean noneGreaterThanFive = numbers.stream().noneMatch(n -> n > 5);
System.out.println(noneGreaterThanFive); // 输出: true
// 检查是否没有负数元素
boolean noneNegativeNumber = numbers.stream().noneMatch(n -> n < 0);
System.out.println(noneNegativeNumber); // 输出: true
}
}
示例一
List<MeoiipAlert> alertInsertList=new ArrayList<MeoiipAlert>();
现在存在一个list列表,里面存在有assetid和quotaid重复的数据,相同的数据按照levels排序,取levels最高的那一条
Map<String, Class> firstInGroup = list.stream()
.sorted(Comparator.comparing(Class::getLevel).reversed())
.collect(Collectors.toMap(
item -> item.getId() + "-" + item.getType(), // 以id和type作为键进行分组
Function.identity(), // 保留原始对象
(existing, replacement) -> existing // 如果存在重复的键,保留先出现的对象
));
List<Class> result = new ArrayList<>(firstInGroup.values());
identity()
就是Function
接口的一个静态方法。
Function.identity()
返回一个输出跟输入一样的Lambda表达式对象,等价于形如t -> t
形式的Lambda表达式。可以保留原始对象类型
(existing, replacement) -> existing
:这部分是一个合并函数,用于处理在Map中已经存在相同键的情况。在这里,我们指定如果遇到重复的键,则保留先出现的对象(existing),而忽略后来出现的对象(replacement)。这样就能保证每组中只保留第一个出现的对象。