Home > Enterprise >  Optimized method of getting list of attributes in Java 8?
Optimized method of getting list of attributes in Java 8?

Time:02-15

My question is an extension of this question.

To get a list of attributes we can use this code:

List<String> namesList = personList.stream()
                                   .map(Person::getName)
                                   .collect(Collectors.toList());

However if I want to get 100 different attributes.

Option #1:

List<Integer> ageList = personList.stream()
                                   .map(Person::getAge)
                                   .collect(Collectors.toList());

List<String> surnamesList = personList.stream()
                                   .map(Person::getSurname)
                                   .collect(Collectors.toList());

List<String> fathersNamesList = personList.stream()
                                   .map(Person::getFathersName)
                                   .collect(Collectors.toList());

List<String> mothersNamesList = personList.stream()
                                   .map(Person::getMothersName)
                                   .collect(Collectors.toList());

then would it be better to rewrite this line 100 times or should I simply run 1 loop and collect all the attributes there.

Option #2:

for(Person person : persons) {
    namesList.add(person.getName());
    surnamesList.add(person.getSurname());
    fathersNamesList.add(person.getFathersName());
    ...
}

CodePudding user response:

Just use a loop:

// Allocate correct size to avoid resizing
List<String> names = new ArrayList<>(persons.size());
List<String> surnames = new ArrayList<>(persons.size());
List<String> ages = new ArrayList<>(persons.size());
// etc

for (Person person : persons) {
    names.add(person.getName);
    surnames.add(person.getSurname);
    ages.add(person.getAge);
    // etc
}

This will out-perform using streams by a very large margin, and it's easy to read and understand.


Minor note: Avoid including the type of a variable in its name, ie prefer names over namesList. Hungarian notation in any form is an anti-pattern.

CodePudding user response:

Duplicating the same logic isn't an option.

For that purpose it's way better to create a generic method that takes two arguments a source list and a function:

    public static void main(String[] args) {
        List<Integer> ageList = getListOfAttributes(personList, Person::getAge);
        List<String> surnamesList = getListOfAttributes(personList, Person::getSurName);
    }
    public static <T, R> List<R> getListOfAttributes(List<T> source,
                                                     Function<T, R> func) {
        return source.stream()
                .map(func)
                .collect(Collectors.toList());
    }

CodePudding user response:

With Streams, a solution that is closer to the loop approach is to create a custom aggregate type:

class AggregatedPersonDetails {
    List<Integer> ageList = new ArrayList<>();
    List<String> surnamesList = new ArrayList<>();
    List<String> fathersnamesList = new ArrayList<>();
    List<String> mothersnamesList = new ArrayList<>();

    public void add(Person person) {
        ageList.add(person.getAge());
        surnamesList.add(person.getSurName());
        fathersnamesList.add(person.getFathersName());
        mothersnamesList.add(person.getMothersName());
    }

    public AggregatedPersonDetails merge(AggregatedPersonDetails other) {
        ageList.addAll(other.ageList);
        surnamesList.addAll(other.surnamesList);
        fathersnamesList.addAll(other.fathersnamesList);
        mothersnamesList.addAll(other.mothersnamesList);
        return this;
    }
}

which you would use as:

personList.stream().collect(
        AggregatedPersonDetails::new,
        AggregatedPersonDetails::add,
        AggregatedPersonDetails::merge);

This allows to do just one iteration.

If you need to do this at multiple places, you can extract a reusable Collector from it:

Collector<Person, AggregatedPersonDetails, AggregatedPersonDetails> collector =
        Collector.of(AggregatedPersonDetails::new, AggregatedPersonDetails::add, AggregatedPersonDetails::merge);

(otherwise you can change the merge() method to return void)

  • Related