Java Streams过滤并替换列表中符合条件的项



我使用的是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映射函数

这里的方法:

您可以尝试以下方法,其中我先用sortedlistid,然后用date按相反的顺序(这样列表的顶部将有对应于每个id的最新记录(,然后使用Collectors.toMap函数创建一个映射,该映射的关键字为id,值为TestClass的对象,同时使用合并函数(a, b) -> a(这将有助于获得与每个键对应的最新值(TreeMap::new将给出排序的映射。

之后,我使用上面创建的映射的.values创建了具有valuesTestClass类型的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]]

最新更新