101

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?

4
  • Hey, I found it just now: stackoverflow.com/questions/737244/… Commented Jun 11, 2012 at 7: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 Commented Jun 11, 2012 at 7:26
  • someone has to apply loop to get it done. either you or some lib that you may use. Commented Jun 11, 2012 at 7: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:) Commented Jun 11, 2012 at 7:39

7 Answers 7

210

Java 8 way of doing it:-

List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

2 Comments

What if I want a list of tuples? Something like [(Id, name),(Id, name)].
Not all 1.8 features are supported for all Android APIs. In particular, java.util.stream is not supported until API 24.
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));

2 Comments

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
how do you extract string ( float,etc) members of the List?
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.

5 Comments

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
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.
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.
guava is definitely more efficient than Bean utils as the later uses reflections..
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.
10

Java 8 lambda expression solution:

List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());

1 Comment

If Java < 1.8 then use Apache CollectionUtils. Example: Collection<String> firstnames = CollectionUtils.collect(userlist, TransformerUtils.invokerTransformer("getFirstname"));
6

If someone get here after a few years:

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

1 Comment

And you just saved me :)
4

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

Comments

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]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.