7

Let's say there is a class:

class Person
{
   String name;
   int age;
   City location;
}

Is there some library that will let me create a list of Strings containing each name from the list of persons in one line instead of creating a new list and looping through the other list?

Something like:

List<Person> people = getAllOfThePeople();
List<String> names = CoolLibrary.createList("name", people);

Rather than:

List<Person> people = getAllOfThePeople();
List<String> names = new LinkedList<String>();
for(Person person : people)
{
    names.add(person.getName());
}
Nick Vikeras
  • 763
  • 2
  • 6
  • 10
  • If only Java had LINQ natively.. No, this is not possible. Maybe it is supported in Java 8, but it certainly isn't prior to that. It's doable with reflection ofcourse, but there's no built-in querying option. – Jeroen Vannevel Dec 03 '13 at 21:48
  • Appears too specific but you can write one using reflection and generics to make the output strongly typed.. – Vikdor Dec 03 '13 at 21:49

2 Answers2

18

You can using Java 8 with lambda expressions :

List<String> listNames = people.stream().map(u -> u.getName()).collect(Collectors.toList());

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class Test {
  public static void main(String args[]){
    List<Person> people = Arrays.asList(new Person("Bob",25,"Geneva"),new Person("Alice",27,"Paris"));
    List<String> listNames = people.stream().map(u -> u.getName()).collect(Collectors.toList());
    System.out.println(listNames);
  }
}
class Person
{
   private String name;
   private int age;
   private String location;

  public Person(String name, int age, String location){
    this.name = name;
    this.age = age;
    this.location = location;
  }

  public String getName(){
    return this.name;
  }

}

Output :

[Bob, Alice]

Demo here.


Alternatively, you can define a method that will take your list as parameter and the function you want to apply for each element of this list :
public static <X, Y> List<Y> processElements(Iterable<X> source, Function <X, Y> mapper) {
    List<Y> l = new ArrayList<>();
    for (X p : source) 
        l.add(mapper.apply(p));
    return l;
}

Then just do :

List<String> lNames = processElements(people, p -> p.getName()); //for the names
List<Integer> lAges = processElements(people, p -> p.getAge()); //for the ages
//etc.

If you want to group people by age, the Collectors class provide nice utilities (example):

Map<Integer, List<Person>> byAge = people.stream()
                                         .collect(Collectors.groupingBy(Person::getAge));
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
4

You can do a little trick using Guava library (which I consider the ultimate Java library that you should be using anyway):

class Person
{
   String name;
   int age;
   City location;

  public static final Function<Person, String> getName = new Function<Person, String>() {
    public String apply(Person person) {
      return person.name;
    }
  }
}


List<Person> people = getAllOfThePeople();
List<String> names = FluentIterable.from(people).transform(Person.getName).toList();

The trick is defining the getName public static Function in the Person class.

siledh
  • 3,268
  • 2
  • 16
  • 29