98

Imagine that I have a list of certain objects:

List<Student>

And I need to generate another list including the ids of Students in the above list:

List<Integer>

Avoiding using a loop, is it possible to achieve this by using apache collections or guava?

Which methods should be useful for my case?

Harshal Parekh
  • 5,918
  • 4
  • 21
  • 43
javatar
  • 4,542
  • 14
  • 50
  • 67
  • Hey, I found it just now: http://stackoverflow.com/questions/737244/java-map-a-list-of-objects-to-a-list-with-values-of-their-property-attributes – javatar Jun 11 '12 at 07:23
  • Jon's answer is pretty cool but if you look at it, it also uses a loop. Any any solution will use a loop - although it might not be visible to you, but internally it will – hage Jun 11 '12 at 07:26
  • someone has to apply loop to get it done. either you or some lib that you may use. – sudmong Jun 11 '12 at 07:30
  • Actually jon's answer does not fit my situation in that i do not wanna use for loop. I think there is a more convenient way over somewhere but waitin for me to find it:) – javatar Jun 11 '12 at 07:39

7 Answers7

209

Java 8 way of doing it:-

List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
Kaushik
  • 3,371
  • 3
  • 23
  • 32
42

With Guava you can use Function like -

private enum StudentToId implements Function<Student, Integer> {
        INSTANCE;

        @Override
        public Integer apply(Student input) {
            return input.getId();
        }
    }

and you can use this function to convert List of students to ids like -

Lists.transform(studentList, StudentToId.INSTANCE);

Surely it will loop in order to extract all ids, but remember guava methods returns view and Function will only be applied when you try to iterate over the List<Integer>
If you don't iterate, it will never apply the loop.

Note: Remember this is the view and if you want to iterate multiple times it will be better to copy the content in some other List<Integer> like

ImmutableList.copyOf(Iterables.transform(students, StudentToId.INSTANCE));
Etienne Neveu
  • 12,604
  • 9
  • 36
  • 59
Premraj
  • 7,802
  • 8
  • 45
  • 66
  • I think it's inelegant to have to define a function separately. I think this can also be done anonymously and in a one step process – Marc Feb 18 '15 at 20:39
  • how do you extract string ( float,etc) members of the List? – sepehr Jul 21 '16 at 12:29
22

Thanks to Premraj for the alternative cool option, upvoted.

I have used apache CollectionUtils and BeanUtils. Accordingly, I am satisfied with performance of the following code:

List<Long> idList = (List<Long>) CollectionUtils.collect(objectList, 
                                    new BeanToPropertyValueTransformer("id"));

It is worth mentioning that, I will compare the performance of guava (Premraj provided) and collectionUtils I used above, and decide the faster one.

javatar
  • 4,542
  • 14
  • 50
  • 67
  • 3
    I will prefer Guava over apache collections irrespective of performance for reasons like - more modern, generics, doesn't use reflection for transformations etc. Overall guava is far more modern that apache collections - read more [here](http://stackoverflow.com/questions/1444437/apache-commons-vs-guava-formerly-google-collections) – Premraj Jun 11 '12 at 17:09
  • 1
    Yes, but actually it does not seem a good idea to me to restrict the usage of it by a compulsory need of extra enum declaration for each type of object I want to transform. – javatar Jun 11 '12 at 19:47
  • Well it depends on your design - You can have interface say `Identifiable` which defines `getId()` method and then you can use this single enum singleton pattern to extract Id commonly. – Premraj Jun 12 '12 at 10:04
  • 2
    guava is definitely more efficient than Bean utils as the later uses reflections.. – chendu Sep 20 '14 at 13:53
  • 2
    Using the property name as a String in the constructor new BeanToPropertyValueTransformer("id") is something I definitely try to avoid. Refactoring gonna be hard! But I'm looking for a solution to workaround this. Meanwhile, I will go with the Guava solution, verbose but safe. – redochka Mar 28 '15 at 12:47
10

Java 8 lambda expression solution:

List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
Romano Zumbé
  • 7,893
  • 4
  • 33
  • 55
Vijay
  • 4,694
  • 1
  • 30
  • 38
  • 1
    If Java < 1.8 then use Apache CollectionUtils. Example: Collection firstnames = CollectionUtils.collect(userlist, TransformerUtils.invokerTransformer("getFirstname")); – Alexei Nov 26 '18 at 17:25
6

If someone get here after a few years:

List<String> stringProperty = (List<String>) CollectionUtils.collect(listOfBeans, TransformerUtils.invokerTransformer("getProperty"));
2

The accepted answer can be written in a further shorter form in JDK 16 which includes a toList() method directly on Stream instances.

Java 16 solution

List<Integer> idList = students.stream().map(Student::getId).toList();
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

You can use Eclipse Collections for this purpose

Student first = new Student(1);
Student second = new Student(2);
Student third = new Student(3);

MutableList<Student> list = Lists.mutable.of(first, second, third);
List<Integer> result = list.collect(Student::getId);

System.out.println(result); // [1, 2, 3]
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89