92

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,381
  • 4
  • 16
  • 39
javatar
  • 4,291
  • 14
  • 49
  • 66
  • 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

197

Java 8 way of doing it:-

List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
Kaushik
  • 3,201
  • 3
  • 20
  • 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,414
  • 9
  • 34
  • 59
Premraj
  • 7,622
  • 8
  • 44
  • 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,291
  • 14
  • 49
  • 66
  • 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.. – chen 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
9

Java 8 lambda expression solution:

List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
Romano Zumbé
  • 7,735
  • 4
  • 32
  • 52
Vijay
  • 4,328
  • 1
  • 27
  • 36
  • 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"));
0

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
  • 20,892
  • 9
  • 46
  • 83
-6

It is Mathematically impossible to do this without a loop. In order to create a mapping, F, of a discrete set of values to another discrete set of values, F must operate on each element in the originating set. (A loop is required to do this, basically.)

That being said:

Why do you need a new list? You could be approaching whatever problem you are solving in the wrong way.

If you have a list of Student, then you are only a step or two away, when iterating through this list, from iterating over the I.D. numbers of the students.

for(Student s : list)
{
    int current_id = s.getID();
    // Do something with current_id
}

If you have a different sort of problem, then comment/update the question and we'll try to help you.

Zéychin
  • 4,001
  • 2
  • 26
  • 27
  • At first thanks for your reply. However, it is dramatic that you said needing a new list is wrong way. Because it is depend on the capacity and range of the need. Therefore, your approach is the wrong way:) Again my mention about "ability to achive that without loop" is about a more convenient way in order not to make code unreadable and low performanced. Anyways, I have already found a way to achive it, going to share in a bit. – javatar Jun 11 '12 at 16:37
  • I did not say that it **is** the wrong way, but it **could** be the wrong way, depending on the problem you are trying to solve. – Zéychin Jun 11 '12 at 20:09