【问题标题】:Using Java 8 lambda expressions, filter a List of objects by property of an inner list使用 Java 8 lambda 表达式,按内部列表的属性过滤对象列表
【发布时间】:2019-01-31 09:48:32
【问题描述】:

我是 Java lambda 表达式的新手,一直在努力解决以下问题:

我正在尝试将以下 Activity 对象列表过滤到具有最近开始时间的单个 Activity:

{
     Name: "activity1"
     Id: "abc-123"
     Fields: [
          { key: "startTime", value: "2018-08-24T12:00:01" },
          { key: "foo", value: "bar" },
          { key: "baz", value: "zoop" }
     ]
},
{
     Name: "activity2"
     Id: "def-456"
     Fields: [
          { key: "bat", value: "zap" },
          { key: "startTime", value: "2018-08-23T12:00:01" },
          { key: "cat", value: "dog" }
     ]
},
{
     Name: "activity3"
     Id: "ghi-789"
     Fields: [
          { key: "flim", value: "flam" },
          { key: "boop", value: "bop" },
          { key: "startTime", value: "2018-08-22T12:00:01" }
     ]
}

基本上,每个对象都有一个内部字段列表,这些字段不按特定顺序出现,我想根据具有最大日期的“startTime”字段将父集合过滤为一项。

对于示例数据,目标是仅返回名称为“activity1”的对象。请注意,字段对象在我的数据中出现的顺序没有特定的顺序。此外,如果一个活动的开始时间与另一个活动的开始时间相匹配,则代码应采用集合中最先出现的项目。

假设活动和字段在一个列表中,并且我的活动类上有getter/setter,这是我开始的,但我知道这是不正确的:

activities().stream().filter(activity ->  
    activity.getFields().stream()
            .filter(field -> field.getKey().equals("startTime"))
            .max((field1, field2) -> {
                Date date1 = Date.from(Instant.parse(field1.getValue()));
                Date date2 = Date.from(Instant.parse(field2.getValue()));
                return date1.compareTo(date2);
            }).isPresent()
).findAny().get();

任何帮助或我应该如何处理的示例将不胜感激。谢谢!


Edit-1: 我正在使用的实际ActivityField 类是从第3 方库中检索的,并不是我自己设计的。以下是它们的外观:

public class Activity {
     private String id;
     private String name;
     private List<Field> fields;

     public String getId() {
         return id;
     }

     public void setId(String id) {
         this.id = id;
     }

     public String getName() {
         return name;
     }

     public void setName(String name) {
         this.name = name;
     }

     public List<Field> getFields() {
         return fields;
     }

     public void setFields(List<Field> fields) {
         this.fields = fields;
     }
 }

public class Field {
     private String key;
     private String value;

     public String getKey() {
         return key;
     }

     public void setKey(String key) {
         this.key = key;
     }

     public String getValue() {
         return value;
     }

     public void setValue(String value) {
         this.value = value;
     }
 }

【问题讨论】:

  • 你能提供你的列表项的类别吗?
  • @EmreSavcı 我已经在问题中添加了edit-1,并举了一个例子。谢谢!
  • 由于您的日期时间字符串没有偏移量,Instant.parse 几乎不起作用。使用LocalDateTime.parse。并且不要使用早已过时且设计不佳的Date 类,因为LocalDateTimeInstant 可以直接比较。

标签: java datetime lambda max filtering


【解决方案1】:

您可能想要定义一个提取日期的函数,然后使用Collectors.maxBy。例如:

// Somewhere in your class:
Instant startDate(Activity activity) {
  return activity.getFields()
    .stream()
    .filter(field -> field.getKey().equals("startTime"))
    .map(field -> Instant.parse(field.getValue()))
    .findAny()
    .get();
}

// .. and then to use it:
Optional<Activity> maxStartDateActivity = activities()
    .stream()
    .max(Comparator.comparing(MyClass::startDate));

旁注:您可能不应该以您的方式表示您的数据。为您的 Activity 值定义一个接口是值得的,它允许您将解析的开始时间作为单个调用获取,然后可以在流管道中使用。这样做将有助于将活动的表示方式与您尝试对它们执行的操作分离开来,这将使您的整个程序更易于阅读,更易于添加更多功能,并且将来更易于更改。

【讨论】:

  • 区别主要是我忘了Stream.max带了一个比较器。更新了两次编辑,谢谢!
  • 感谢@jacobm 的输入。我在最初的问题中没有提到数据结构不是我选择的,这是由于我从 3rd 方库中检索的一些结果。对不起,我应该包括那个细节。在任何一种情况下,我都能够创建一个 startDate 函数,并且使用您的方法它的工作原理相同。该函数不属于Activity 类。谢谢!
猜你喜欢
  • 2018-06-07
  • 2015-07-12
  • 1970-01-01
  • 2020-07-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多