需要通过谓词过滤列表

问题描述 投票:0回答:3

亲爱的 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(),但没有令人满意的结果。

java list dictionary arraylist filtering
3个回答
0
投票

由于稳定性是一项要求(必须保留列表的顺序),因此不能选择使用 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 中。


0
投票

这可以使用传统的 for 循环或具有函数式风格的奇特流来完成。

但是第一件事是我们如何定义密钥,可以很简单

record ItemKey(int year, Month month, BigDecimal value) {}
使用记录。

For循环方法

这非常简单,我们使用 Set 来跟踪

ItemKey

如果访问了
ItemKey
,则继续下一个
Item
否则将
ItemKey
添加到集合中,并保留
Item

功能风格:

  1. 将具有相同 ItemKey 的 Item 分组到 Map
  2. 在每个条目中

对于这个问题,哪种方法更好没有绝对的答案,我建议选择您更舒服的方式。对于进行简单的过滤、映射,函数式方法很方便并且不易出错。但对于更复杂的问题,使用循环通常更容易找到解决方案。

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;
    }

}

0
投票
  1. 创建一个 [Java]
    record
    ,其中包含一个
    LocalDateTime
    、一个
    BigDecimal
    和一个
    String
  2. 覆盖
    equals
    中的方法
    record
  3. 覆盖
    hashCode
    中的方法
    record
  4. 创建
    List
    record
  5. 流式传输
    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]
© www.soinside.com 2019 - 2024. All rights reserved.