我有一个包含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个不同类型的项目(按修改日期降序排列)。
答案 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);
我假设modificationDate
是LocalDateTime
字段,否则,如果它是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
)。您可以反转分区谓词,使动物先行,而无需更改代码中的任何其他内容。