Java Lambda-具有两个单独条件的过滤器列表

时间:2019-09-02 12:55:04

标签: java lambda java-stream

我有一个包含3个对象的列表,按修改日期排序,例如:

{id=1, name=apple, type=fruit, modificationDate=2019-09-02}
{id=2, name=potato, type=vegetable, modificationDate=2019-06-12}
{id=3, name=dog, type=animal, modificationDate=2018-12-22}

我需要做的是以这种方式过滤这些项目:动物类型的对象将始终传递到新列表,但是只有水果和蔬菜类型的一项(具有最近修改日期的项目)将被传递到新列表。通过,因此结果列表如下:

{id=1, name=apple, type=fruit, modificationDate=2019-09-02}
{id=3, name=dog, type=animal, modificationDate=2018-12-22}

我试图在流操作上结合使用findFirst()和filter(),尽管如此,我只能设法使其一个接一个地工作,而不是作为“或”条件。

它适用于这样的代码:

    List<Item> g = list.stream().filter(f -> f.getType() == animal).collect(Collectors.toList());
    Item h = list.stream().filter(f -> f.getType() != animal).findFirst().get();

    g.add(h);

但这是一个极其丑陋的解决方案,所以我正在寻找更优雅的东西。

任何帮助!

PS。该列表将始终仅包含3个项目,其中1个为动物类型(应保留)和2个不同类型的项目(按修改日期降序排列)。

4 个答案:

答案 0 :(得分:3)

您可以使用Stream.concat来加入Item的两个流,一个是animal类型,另一个是非动物类型。然后以Item降序对非动物类型modificationDate的流进行过滤和排序,并通过放置limit(1)来获取第一个动物。

然后,当流合并后,您再次对其进行排序,这一次是在动物类型的Item流和非动物类型的Item流之间进行

Comparator<Item> comp = Comparator.comparing(Item::getModificationDate, Comparator.reverseOrder());
List<Item> sorted = Stream.concat(list.stream()
                                      .filter(f -> "animal".equals(f.getType())), 
                                  list.stream()
                                      .filter(f -> !"animal".equals(f.getType()))
                                      .sorted(comp)
                                      .limit(1))
                             .sorted(comp)
                             .collect(Collectors.toList());

System.out.println(sorted);

我假设modificationDateLocalDateTime字段,否则,如果它是String,则排序将按词法顺序进行。

答案 1 :(得分:1)

如果您试图找到不需要的物品,然后将其从列表中删除,而不是进行复杂的过滤,该怎么办? 我想这很好用:

@Test
  public void testTest() {
    List<TestClass> list = new ArrayList<>(Arrays.asList(new TestClass("animal", new Date())
        , new TestClass("fruit", DateUtils.addDays(new Date(), -2))
        , new TestClass("veg", DateUtils.addDays(new Date(), -1))));
    TestClass toRemove = list.stream().filter(item -> !Objects.equals("animal", item.getName()))
        .min(Comparator.comparing(TestClass::getDate)).get();
    list.remove(toRemove);

    System.out.println(list);
  }

  class TestClass {
    private String name;
    private Date date;

    public TestClass(String name, Date date) {
      this.name = name;
      this.date = date;
    }

    public String getName() {
      return name;
    }

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

    public Date getDate() {
      return date;
    }

    public void setDate(Date date) {
      this.date = date;
    }

    @Override public String toString() {
      final StringBuilder sb = new StringBuilder("TestClass{");
      sb.append("name='").append(name).append('\'');
      sb.append(", date=").append(date);
      sb.append('}');
      return sb.toString();
    }
  }

答案 2 :(得分:1)

另一种方法是对列表进行两次排序。首先对列表进行排序,以使动物类型的元素始终位于列表的开头,然后按修改日期列出其他元素。然后将列表限制为两个元素,然后按修改日期再次排序。我在这里使用的“动物”是按字母顺序在其他类型之前,但是您也可以实现一个简单的逻辑,使动物<水果/蔬菜

Function<Item,Integer> byType = i -> i.type.equals("animal")?1:0;
List<Item> myList = list.stream()
                        .sorted(Comparator.comparing(byType)
                                .thenComparing(Item::getModificationDate).reversed())
                        .limit(2)
                        .sorted(Comparator.comparing(Item::getModificationDate).reversed())
                        .collect(Collectors.toList());

这里的优点是可以一步完成。

答案 3 :(得分:1)

我需要做的是以这种方式过滤这些项目:动物类型的对象将始终传递到新列表,但是只有水果和蔬菜类型的一项(具有最近修改日期的项目)将被传递到新列表。通过,因此结果列表如下:

如果我理解这项权利,您正在寻找的是(简化的):从给定的列表中,获得动物类型的项目和非动物类型的项目,仅保留上次修改日期最大的项目。 / p>

我将继续介绍动物项的侧面实现细节,该项始终是列表中的单个项。这意味着,您可以partiton向下游maxBy进行流传输,这两者都将动物项目保留在其中,并且它将找到正确的非动物项目:

List<Item> input = ... // your list here

// a small inconvenience with types here, because maxBy's
// reduction type is Optional
Map<Boolean, Optional<Item>> map = list.stream()
    .collect(Collectors.partitioningBy(
            item -> "animal".equals(item.getType()),
            Collectors.maxBy(Comparator.comparing(Item::getLastModifiedDate))
        )
    );

List<Item> result = new ArrayList<>(2);
map.values().forEach(option -> option.ifPresent(result::add));

consume(result);

注意:在我的机器上,它将非动物放在最前面,因为迭代迭代了分区收集器的结果(该映射的第一个条目用于Boolean.FALSE)。您可以反转分区谓词,使动物先行,而无需更改代码中的任何其他内容。

相关问题