我使用的是Java 8流,如果存在多个具有相同房间编号的项目并使用最新日期的项目,我想删除列表中的一个项目。
我有这个清单:
final List<TestClass> testClasses = Arrays.asList(
new TestClass(100L, LocalDate.of(2020, 1, 10)),
new TestClass(100L, LocalDate.of(2019, 1, 10)),
new TestClass(101L, LocalDate.of(2020, 7, 20)),
new TestClass(102L, LocalDate.of(2020, 1, 9)),
new TestClass(103L, LocalDate.of(2020, 1, 11)),
new TestClass(104L, LocalDate.of(2020, 2, 13))
);
我想过滤这个列表,这样,如果roomNumber(TestClass构造函数的第一个参数(在列表中出现多次,那么应该删除该条目,并保存最新日期的条目。
过滤后从上述列表输出的样本:
[
TestClass(100L, LocalDate.of(2020, 1, 10)),
TestClass(101L, LocalDate.of(2020, 7, 20)),
TestClass(102L, LocalDate.of(2020, 1, 9)),
TestClass(103L, LocalDate.of(2020, 1, 11)),
TestClass(104L, LocalDate.of(2020, 2, 13))
]
正如您所看到的,roomNumber为100的第二个TestClass对象已被删除,剩下的是roomNumber也为100的第一个TestClass物体,但我们保留了它,因为日期比其他条目更高或更近。
我已经阅读了Java流文档,并且知道过滤。有没有一种方法可以使用Java流来实现这一功能?我觉得有,但可能有一种我不知道的方法来使用这个api。
TIA,如果需要更多信息,请告诉我:(
编辑:这就是我的尝试。我了解了groupingBy函数,并能够将TestClass放入按房间编号分组的列表中。我不知道从那里去哪里。
Map<Long, List<TestClass>> c = testClasses.stream()
.collect(Collectors.groupingBy(TestClass::getRoomNumber));
您做了一次伟大的尝试!您可以通过使用日期获取最大值,将每个值(TestClass
实例列表(转换为单个TestClass
实例来继续处理映射
List<TestClass> c = testClasses.stream()
.collect(Collectors.groupingBy(TestClass::getRoomNumber))
.values() // returns the collection of values from the map
.stream() // converts that collection to a stream
.map(group -> // converts each item (a list of TestClass for a given room number)
group.stream() // converts that list to a stream
.max( // returns the maximum value of the list using the following Comparator
Comparator.comparing(TestClass::getDate)
// creates a comparator for TestClass based on calling getDate on each item,
// using the natural ordering for LocalDate
) // This returns Optional<TestClass>
) // This returns Stream<Optional<TestClass>>
.filter(Optional::isPresent) // This removes empty Optional values
.map(Optional::get) // Converts the non-empty Optional to the value it contains
.collect(Collectors.toList()); // Collects the resulting stream into a list
需要进一步澄清的一点是:为什么max
返回Optional<TestClass>
?这将处理空流(换句话说,映射中没有TestClass
项目的房间编号(的情况,在这种情况下,将返回空的Optional
。现在,我们知道这不可能发生,因为数据结构的构建方式,但编译器无法推断出这一点,因此需要处理空值的不可能情况。严格地说,filter
部分可以被删除,但拥有它将确保代码在未来出现允许空值的变化时是稳健的。
使用Java Streams映射函数
这里的方法:
您可以尝试以下方法,其中我先用sorted
、list
、id
,然后用date
按相反的顺序(这样列表的顶部将有对应于每个id的最新记录(,然后使用Collectors.toMap
函数创建一个映射,该映射的关键字为id,值为TestClass的对象,同时使用合并函数(a, b) -> a
(这将有助于获得与每个键对应的最新值(和TreeMap::new
将给出排序的映射。
之后,我使用上面创建的映射的.values
创建了具有values
的TestClass
类型的new ArrayList
。
public class Test {
public static void main(String[] args) {
final List<TestClass> testClasses = Arrays.asList(
new TestClass(100L, LocalDate.of(2020, 1, 10)),
new TestClass(100L, LocalDate.of(2019, 1, 10)),
new TestClass(101L, LocalDate.of(2020, 7, 20)),
new TestClass(102L, LocalDate.of(2020, 1, 9)),
new TestClass(103L, LocalDate.of(2020, 1, 11)),
new TestClass(104L, LocalDate.of(2020, 2, 13))
);
List<TestClass> uniqueListWithLatestData =
new ArrayList<>(testClasses.stream()
.sorted((Comparator.comparing(TestClass::id)
.thenComparing(TestClass::date).reversed()))
.collect(Collectors.toMap(TestClass::id, x -> x,(a, b) -> a,
TreeMap::new)).values());
System.out.println(uniqueListWithLatestData);
}
}
注意:
在这里,我为解决方案的TestClass创建了一个record
,您可以拥有自己的TestClass POJO(可从JDK14获取记录(
public record TestClass(long id, LocalDate date) {}
输出:
[TestClass[id=100, date=2020-01-10],
TestClass[id=101, date=2020-07-20],
TestClass[id=102, date=2020-01-09],
TestClass[id=103, date=2020-01-11],
TestClass[id=104, date=2020-02-13]]