39

I'm learning lambda right now, and I wonder how can I write this code by a single line with lambda.

I have a Person class which includes an ID and name fields

Currently, I have a List<Person> which stores these Person objects. What I want to accomplish is to get a string consisting of person's id just like.

"id1,id2,id3".

How can I accomplish this with lambda?

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
winhell
  • 471
  • 1
  • 6
  • 11

4 Answers4

60

To retrieve a String consisting of all the ID's separated by the delimiter "," you first have to map the Person ID's into a new stream which you can then apply Collectors.joining on.

String result = personList.stream().map(Person::getId)
                          .collect(Collectors.joining(","));

if your ID field is not a String but rather an int or some other primitive numeric type then you should use the solution below:

String result = personList.stream().map(p -> String.valueOf(p.getId()))
                          .collect(Collectors.joining(","));
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
5

stream to map, and collect to list!

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

if you need that in a String object then join the list

String res = String.join(" , ", myListStringId);
System.out.println(res);
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
2

You can also using Collectors#mapping as below:

String ids = personList.stream().collect(mapping(Person::getId
      , Collectors.joining(",")));

OR Person.ID is not an instance of CharSequence then you need double mapping as below:

String ids = personList.stream().collect(mapping(Person::getId
    , mapping(String::valueOf
        , Collectors.joining(","))));
holi-java
  • 29,655
  • 7
  • 72
  • 83
1

Slightly over-engineered, but helpful to have if this issue occurs more often (especially if you add overloaded methods that delegate to this one using default mappers and separators):

/**
 * @param separator used to join the values. NOTE: the separator is interpreted as a regular expression.
 * @return a list of the values' string representation according to <code>mapper</code>, separated by the specified
 *         string. Null if list is null or empty.
 */
public static <R> String toListString(Collection<R> list, String separator,
                                      Function<? super R, ? extends String> mapper)
{
    if (list == null || list.isEmpty())
    {
        return null;
    }

    return list.stream()
               .map(mapper)
               .collect(Collectors.joining(separator));
}

and the appropriate inverse funtion:

/**
 * @param list a list of values, separated by the specified separator
 * @param separator used to join the values. NOTE: the separator is interpreted as a regular expression.
 * @param mapper the function to map a single string to a value.
 * @return a list of the values. Empty if string is null or empty.
 */
public static <R> List<R> fromListString(String list, String separator,
                                         Function<? super String, ? extends R> mapper)
{
    if (list == null || list.isEmpty())
    {
        return new ArrayList<>();
    }

    return Arrays.stream(list.trim().split(separator))
                 .map(mapper)
                 .collect(Collectors.toCollection(ArrayList::new));
}

If performance is an issue I would opt for the classic loop approach:

    StringBuilder s = new StringBuilder();
    for(R r : list){
        if (s.length() != 0)
            s.append(separator);
        s.append(mapper.apply(r));
    }
    return s.toString();

and:

    List<R> result = new ArrayList<>();
    for (String s : list.trim().split(separator)){
        result.add(mapper.apply(s));
    }
    return result;
Malte Hartwig
  • 4,477
  • 2
  • 14
  • 30