如何在Java中基于小时间隔删除重复列表?



首先,我有这个对象我叫它MyObject

;
public class MyObject{
private google.protobuf.Timestamp timestamp;
private String description;
}

然后我有这个列表:

List<MyObject> myList = new ArrayList<>();

现在让我们假设myList包含500个项目。我想要的是消除在同一小时内出现的重复(相同的描述)。

因此,具有相同描述的两个不同项目不应该在同一小时内同时出现在列表中。如果有,我们只保留一个,删除另一个。

的例子:

如果列表包含以下两项:

06-07-2022T01:30:00, "some random description"06-07-2022T01:35:00, "some random description"

然后我们想要删除其中一个,因为它们具有相同的描述并且在同一小时内。

但是如果我们有这个:

06-07-2022T01:30:00, "some random description"06-07-2022T03:20:00, "some random description"

那么我们不想删除它们中的任何一个,因为它们不在同一小时内。

我该怎么做?

根据您在评论中给出的澄清,我使用LocalDateTime来简化示例条目并检索小时,但我确信google.protobuf.Timestamp可以转换为适当的日期并提取其小时。

为了根据描述,日期和小时只保留一个对象,我在POJO中添加了一个助手方法,以获得这些字段的连接,然后按其结果值分组,以便获得Map,其中每个键(描述,日期和小时)只有一个关联的对象。最后,我将Map的值收集到List中。

List<MyObject> list = new ArrayList<>(List.of(
new MyObject(LocalDateTime.parse("06-07-2022T01:30:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description"),
new MyObject(LocalDateTime.parse("06-07-2022T01:35:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description"),
new MyObject(LocalDateTime.parse("06-07-2022T03:20:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description"),
new MyObject(LocalDateTime.parse("06-07-2022T04:30:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description2"),
new MyObject(LocalDateTime.parse("06-07-2022T04:35:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description2"),
new MyObject(LocalDateTime.parse("06-07-2022T06:20:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description2"),
new MyObject(LocalDateTime.parse("08-07-2022T01:30:00", DateTimeFormatter.ofPattern("dd-MM-yyyy'T'HH:mm:ss")), "some random description")
));
List<MyObject> listRes = list.stream()
.collect(Collectors.toMap(
obj -> obj.getDescrDateHour(),
Function.identity(),
(obj1, obj2) -> obj1
))
.values()
.stream().
collect(Collectors.toList());
<<h4> POJO类/h4>
class MyObject {
private LocalDateTime timestamp;
private String description;
public MyObject(LocalDateTime timestamp, String description) {
this.timestamp = timestamp;
this.description = description;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
public String getDescription() {
return description;
}
public String getDescrDateHour() {
return description + timestamp.toLocalDate().toString() + timestamp.getHour();
}
@Override
public String toString() {
return timestamp + " - " + description;
}
}

这里是测试代码的链接

https://www.jdoodle.com/iembed/v0/sZV

Input: 
2022-07-06T01:30 - some random description
2022-07-06T01:35 - some random description
2022-07-06T03:20 - some random description
2022-07-06T04:30 - some random description2
2022-07-06T04:35 - some random description2
2022-07-06T06:20 - some random description2
2022-07-08T01:30 - some random description
Output: 
2022-07-06T04:30 - some random description2
2022-07-08T01:30 - some random description
2022-07-06T06:20 - some random description2
2022-07-06T03:20 - some random description
2022-07-06T01:30 - some random description

一个非常简单的解决方案是HashMap。使用描述作为键,使用时间戳作为值。所以你总是只保存最后一个时间戳到给定的描述,并自动覆盖它。

如果你想保存你的对象,我只需按日期排序列表,然后填写HashMap并将HashMap再次转换为list。它没有最好的性能,但它很容易。你可以使用函数式Java按日期对集合进行排序

您可以定义一个相等计算类(或者在MyObject类中执行,取决于它实际表示什么),并使用它根据相等定义查找惟一值。在这种情况下,相等意味着:相同的描述和相同的时间戳,每小时的精度。

这里有一个例子(可能需要一些调整,只是一个概念演示):


class UniqueDescriptionWithinHourIdentifier {
// equals and hashCode could also be implemented in MyObject
// if it's only purpose is data representation
// but a separate class defines a more concrete abstraction
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHH");
private Date timestamp;
private String description;
UniqueDescriptionWithinHourIdentifier(MyObject object) {
timestamp = object.timestamp;
description = object.description;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
var other = (UniqueDescriptionWithinHourIdentifier) object;
return description.equals(other.description)
// compare the timestamps however you want - format used for simplicity
&& DATE_FORMAT.format(timestamp)
.equals(DATE_FORMAT.format(other.timestamp));
}
@Override
public int hashCode() {
// cannot contain timestamp - a single hash bucket will contain multiple elements 
// with the same definition and the equals method will filter them out
return Objects.hashCode(description);
}
}
class MyObjectService {
// here a new list without duplicates is calculated  
List<MyObject> withoutDuplicates(List<MyObject> objects) {
return List.copyOf(objects.stream()
.collect(toMap(UniqueDescriptionWithinHourIdentifier::new,
identity(),
(e1, e2) -> e1,
LinkedHashMap::new))
.values());
}
}

添加equals&hashcode方法的MyObject类与equals有一些逻辑如下:

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyObject other = (MyObject) obj;

Calendar calendar = Calendar.getInstance();
calendar.setTime(timestamp);
int hour1=calendar.HOUR;
int date1 = calendar.DATE;
calendar.setTime(other.timestamp);
int hour2 = calendar.HOUR;
int date2  =calendar.DATE;
return Objects.equals(hour1, hour2) && Objects.equals(date1, date2);
}

这里,基本上我是检查两个对象是否有相同的小时&日期,如果是,就忽略另一个对象。

一旦你这样做了,你可以使用:

List<MyObject> myList = new ArrayList<>();
myList.stream().distinct().collect(Collectors.toList()); // returns you new distinct objects list.

请注意,对于这种情况,您可以使用通过编辑器生成的hashCode的默认实现。流的distinct()方法正在检查您是否有equals&hashcode可用于底层流类。

注意:您可以扩展equals来检查日,日,月,年等,以验证确切的日期。

最新更新