亲爱的 Stackoverflow 会员,
问候。
我有一个包含多个项目的列表,每个项目都包含一个时间戳、一个 BigInt 值和一个字母代码,如下所示:
时间戳 | 价值 | 代码 |
---|---|---|
2024-01-10 00:20:00 | 105.50 | 我 |
2024-01-15 02:30:00 | 105.50 | A |
2024-01-16 10:45:00 | 110.50 | A |
2024-02-01 12:20:00 | 111.25 | 我 |
2024-02-04 04:35:00 | 111.25 | A |
2024-02-25 02:15:00 | 112.25 | A |
我需要选择所有年、月、值相同的记录;将是重复出现的情况。在这些重复项中,我只想保留第一个,并消除其他项。
Code
值可以是“A”或“I”,无所谓。非重复记录必须保持与原始列表相同的顺序。过滤列表的示例:
时间戳 | 价值 | 代码 |
---|---|---|
2024-01-10 00:20:00 | 105.50 | 我 |
2024-01-16 10:45:00 | 110.50 | A |
2024-02-01 12:20:00 | 111.25 | 我 |
2024-02-25 02:15:00 | 112.25 | A |
我该怎么做?我很感激你能给我的任何建议。谢谢。
我尝试过.filter()和.map(),但没有令人满意的结果。
由于稳定性是一项要求(必须保留列表的顺序),因此不能选择使用 list.stream().distinct() 。
如果您的
equals
方法准确检查提到的三列,您可以执行以下操作:
ArrayList result = new ArrayList();
original.stream().forEach(i -> if (!result.contains(i)) result.add(i));
如果不适合使用 equals 方法,可以通过比较三个值来检查结果列表中的所有项目。
ArrayList result = new ArrayList();
original.stream().forEach(i -> {
for (YourClass j : result) {
if (<all three values are equal>) return;
}
result.add(i);
});
注意:如果列表很大并且计算时间(此代码为 O(n^2))是一个问题,您可以使用三列值创建一个新值并将其放入 HashSet。然后,您可以调用 HashSet 上的 contains 并将值 i 添加到结果集和 HashSet 中。
这可以使用传统的 for 循环或具有函数式风格的奇特流来完成。
但是第一件事是我们如何定义密钥,可以很简单
record ItemKey(int year, Month month, BigDecimal value) {}
使用记录。
这非常简单,我们使用 Set 来跟踪
ItemKey
ItemKey
,则继续下一个 Item
否则将 ItemKey
添加到集合中,并保留 Item
对于这个问题,哪种方法更好没有绝对的答案,我建议选择您更舒服的方式。对于进行简单的过滤、映射,函数式方法很方便并且不易出错。但对于更复杂的问题,使用循环通常更容易找到解决方案。
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
public class GroupFindFirst {
// define how your key
record ItemKey(int year, Month month, BigDecimal value) {
}
public static void main(String[] args) {
List<Item> items = List.of(
new Item(LocalDateTime.parse("2024-01-10T00:20:00"), new BigDecimal("105.50"), "I"),
new Item(LocalDateTime.parse("2024-01-15T02:30:00"), new BigDecimal("105.50"), "A"),
new Item(LocalDateTime.parse("2024-01-16T10:45:00"), new BigDecimal("110.50"), "A"),
new Item(LocalDateTime.parse("2024-02-01T12:20:00"), new BigDecimal("111.25"), "I"),
new Item(LocalDateTime.parse("2024-02-04T04:35:00"), new BigDecimal("111.25"), "A"),
new Item(LocalDateTime.parse("2024-02-25T02:15:00"), new BigDecimal("112.25"), "A")
);
System.out.println("filter using for loop");
filterUsingForLoop(items).forEach(System.out::println);
System.out.println("filter using stream");
filterUsingStream(items).forEach(System.out::println);
}
private static List<Item> filterUsingForLoop(List<Item> items) {
Set<ItemKey> itemKeys = new HashSet<>();
List<Item> filtered = new ArrayList<>();
for (Item item : items) {
// add return true when ItemKey not exist before
if (itemKeys.add(new ItemKey(item.timestamp.getYear(), item.timestamp.getMonth(), item.value))) {
filtered.add(item);
}
}
return filtered;
}
private static List<Item> filterUsingStream(List<Item> items) {
return items.stream().collect(collectingAndThen(
groupingBy(
item -> new ItemKey(item.timestamp.getYear(), item.timestamp.getMonth(), item.value),
LinkedHashMap::new, // to preserve order
mapping(Function.identity(), toList())),
itemKeyToListMap -> itemKeyToListMap.values().stream().map(list -> list.get(0)).toList()
));
}
public static class Item {
public Item(LocalDateTime timestamp, BigDecimal value, String code) {
this.timestamp = timestamp;
this.value = value;
this.code = code;
}
@Override
public String toString() {
return "Item{" +
"timestamp=" + timestamp +
", value=" + value +
", code='" + code + '\'' +
'}';
}
private LocalDateTime timestamp;
private BigDecimal value;
private String code;
}
}
record
,其中包含一个 LocalDateTime
、一个 BigDecimal
和一个 String
。equals
中的方法 record
。hashCode
中的方法 record
。List
的 record
。List
并收集到 LinkedHashSet
,因为它将删除重复项(根据覆盖的 equals
方法确定)并保持 record
的添加顺序。import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
public class MyClass {
public static void main(String args[]) {
List<TsValCod> list = new ArrayList<>();
list.add(new TsValCod(LocalDateTime.of(2024, 1, 10, 0, 20, 0), new BigDecimal(105.50), "I"));
list.add(new TsValCod(LocalDateTime.of(2024, 1, 15, 2, 30, 0), new BigDecimal(105.50), "A"));
list.add(new TsValCod(LocalDateTime.of(2024, 1, 16, 10, 45, 0), new BigDecimal(110.50), "A"));
list.add(new TsValCod(LocalDateTime.of(2024, 2, 1, 12, 20, 0), new BigDecimal(111.25), "I"));
list.add(new TsValCod(LocalDateTime.of(2024, 2, 4, 4, 35, 0), new BigDecimal(111.25), "A"));
list.add(new TsValCod(LocalDateTime.of(2024, 2, 25, 2, 15, 0), new BigDecimal(112.25), "A"));
Object temp = list.stream()
.collect(Collectors.toCollection(() -> new LinkedHashSet()));
Collection<?> set = (Collection<?>) temp;
set.stream()
.forEach(System.out::println);
}
}
record TsValCod(LocalDateTime ts, BigDecimal val, String code) {
public boolean equals(Object obj) {
boolean equal = this == obj;
if (!equal) {
if (obj != null && getClass().equals(obj.getClass())) {
TsValCod other = (TsValCod) obj;
LocalDateTime meTs = ts();
LocalDateTime otherTs = other.ts();
BigDecimal meVal = val();
BigDecimal otherVal = other.val();
if (meTs == null) {
if (otherTs == null) {
equal = true;
}
else {
equal = false;
}
}
else {
if (otherTs == null) {
equal = false;
}
else {
int meYear = meTs.getYear();
Month meMonth = meTs.getMonth();
int otherYear = otherTs.getYear();
Month otherMonth = otherTs.getMonth();
equal = meYear == otherYear && meMonth.equals(otherMonth);
}
}
if (equal) {
if (meVal == null) {
if (otherVal == null) {
equal = true;
}
else {
equal = false;
}
}
else {
if (otherVal == null) {
equal = false;
}
else {
equal = meVal.equals(otherVal);
}
}
}
}
}
return equal;
}
@Override
public int hashCode() {
int year = ts.getYear();
int month = ts.getMonthValue();
YearMonth ym = YearMonth.of(year, month);
return ym.hashCode() + val.hashCode();
}
}
运行上面代码时的输出:
(重写
toString
的 TsValCod
方法来自定义输出。)
TsValCod[ts=2024-01-10T00:20, val=105.5, code=I]
TsValCod[ts=2024-01-16T10:45, val=110.5, code=A]
TsValCod[ts=2024-02-01T12:20, val=111.25, code=I]
TsValCod[ts=2024-02-25T02:15, val=112.25, code=A]