过滤列表对于给定对象属性唯一的优雅解决方案

时间:2014-06-17 21:51:16

标签: java guava

我有一个扁平的多地图(列表)对象,我想将其转换为一个列表,其中'key'属性/字段(例如下面的 name )在所有条目中都是唯一的。

如果多个条目具有相同的密钥(名称),则应选择具有最大 creationDate 字段的条目。

示例:

List<Object> myList =
[
  {name="abc", age=23, creationDate = 1234L},
  {name="abc", age=12, creationDate = 2345L},
  {name="ddd", age=99, creationDate = 9999L}
]

应转换为:

List<Object> = 
[
  {name="abc", age=12, creationDate = 2345L},
  {name="ddd", age=99, creationDate = 9999L}
]

是否有一种优雅的方式(可能使用Guava库?)在Java中解决这个问题?我意识到我可以尝试使用带有 name 的HashMap作为查找所有唯一条目的键,但我觉得有更好的方法可以解决这个问题。

谢谢!

3 个答案:

答案 0 :(得分:3)

如果您有可能使用Java 8,我会推荐Streams,因为已经告知了其他答案。 如果没有,你可以选择这样的东西。

首先,您通过creationDate对List desending进行排序。然后,您创建一个TreeSet,将所有具有相同名称的人视为相等。因此,只会添加第一个(最高的)creationDate,而忽略其他的。

List<Person> persons = new ArrayList<>();
persons.add(new Person("abc", 23, 1234L));
persons.add(new Person("abc", 12, 2345L));
persons.add(new Person("def", 99, 9999L));

Collections.sort(persons, new Comparator<Person>() {
    public int compare(Person o1, Person o2) {
            return (int) (o2.creationDate - o1.creationDate);
    }
});

Set<Person> personsHashed = new TreeSet<>(new Comparator<Person>() {
    public int compare(Person o1, Person o2) {
        return o2.name.compareTo(o1.name);
    }
});
personsHashed.addAll(persons);

答案 1 :(得分:2)

这是否被认为是优雅的是相当主观的,但这里是:

static List<Person> byName(Collection<Person> persons) {
    Map<String, Person> personsByName = persons.stream().collect(
        Collectors.groupingBy(Person::getName,
            Collectors.collectingAndThen(
                Collectors.maxBy(
                    Comparator.comparingLong(Person::getCreationDate)),
                p -> p.get())));
    return new ArrayList<Person>(personsByName.values());
}

通过对收集器和比较器的方法使用静态导入,可以使其更具可读性,例如:

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.maxBy;
import static java.util.Comparator.comparingLong;

然后代码可以略微缩短为:

static List<Person> byName(Collection<Person> persons) {
    Map<String, Person> personsByName =
        persons.stream().collect(
            groupingBy(Person::getName, collectingAndThen(
                maxBy(comparingLong(Person::getCreationDate)),
                p -> p.get())));
    return new ArrayList<Person>(personsByName.values());
}

答案 2 :(得分:1)

在Java 8中,您可以使用新的Streaming API将集合Stream通过过滤器,然后生成结果。那应该能够做你想要的。

或者,您已考虑将它们放入HashMap的想法看起来非常简单,并且会提供您需要的行为。我不会试着过度思考这个:)