Elegant solution for filtering a list to be unique for given object attribute

馋奶兔 提交于 2019-12-23 14:20:36

问题


I have a flattened multi-map (a list) of objects which I would like to convert to a list where a 'key' attribute / field (e.g. name below) is unique amongst all entries.

In the case where multiple entries have the same key (name) the entry with the largest creationDate field should be picked.

Example:

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

Should be converted to:

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

Is there an elegant way (possibly using Guava libraries?) to solve this in Java? I realize I can just try and use a HashMap with name as the key to find all unique entries, but I get the feeling there is a better way to solve this.

Thanks!


回答1:


If you have the possibility to work with Java 8 I would recommend Streams as the other answers allready told. If not you can go with something like this.

First you sort the List desending by the creationDate. Then you create a TreeSet witch treats all Persons with same name as equal. Therefore only the first (highest) creationDate will be added and further ones ignored.

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



回答2:


Whether this is considered elegant is rather subjective, but here goes:

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

You can make it slightly more readable by using static imports for the methods of Collectors and Comparator, such as:

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;

Then the code can be slightly shortened to:

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



回答3:


In Java 8 you can use the new Streaming API to take the collection Stream it through filters and then produce your result. That should be able to do what you are looking for.

Alternatively the idea you already considered of dropping them into a HashMap seems pretty straightforward and will give the behaviour you need. I wouldn't try and over-think this :)



来源:https://stackoverflow.com/questions/24273891/elegant-solution-for-filtering-a-list-to-be-unique-for-given-object-attribute

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!