Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert a for loop to concat String into a lambda expression

I have the following for loop which iterates through a list of strings and stores the first character of each word in a StringBuilder. I would like to know how can I transform this to a lambda expression

StringBuilder chars = new StringBuilder();
for (String l : list) {
    chars.append(l.charAt(0));
}  
like image 492
Faktor 10 Avatar asked Jul 16 '15 14:07

Faktor 10


People also ask

How do I concatenate two strings in a for loop in Java?

str1 = str2 + str2; Since Strings are immutable in java, instead of modifying str1 a new (intermediate) String object is created with the concatenated value and it is assigned to the reference str1. If you concatenate Stings in loops for each iteration a new intermediate object is created in the String constant pool.

Is lambda function faster than for loop?

The answer is it depends. I have seen cases where using a lambda was slower and where it was faster. I have also seen that with newer updates you get more optimal code.


5 Answers

Assuming you call toString() on the StringBuilder afterwards, I think you're just looking for Collectors.joining(), after mapping each string to a single-character substring:

String result = list
    .stream()
    .map(s -> s.substring(0, 1))
    .collect(Collectors.joining());

Sample code:

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

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("foo");
        list.add("bar");
        list.add("baz");
        String result = list
            .stream()
            .map(s -> s.substring(0, 1))
            .collect(Collectors.joining());
        System.out.println(result); // fbb
    }
}

Note the use of substring instead of charAt, so we still have a stream of strings to work with.

like image 96
Jon Skeet Avatar answered Oct 02 '22 17:10

Jon Skeet


Tons of ways to do this - the most simple option: stick to adding to a StringBuilder and do this:

StringBuilder chars = new StringBuilder();

list.forEach(l -> chars.append(l.charAt(0)));
like image 31
Fritz Duchardt Avatar answered Oct 02 '22 17:10

Fritz Duchardt


Without creating many intermediate String objects you can do it like this:

StringBuilder sb = list.stream()
                       .mapToInt(l -> l.codePointAt(0))
                       .collect(StringBuilder::new, 
                                StringBuilder::appendCodePoint, 
                                StringBuilder::append);

Note that using codePointAt is much better than charAt as if your string starts with surrogate pair, using charAt you may have an unpredictable result.

like image 24
Tagir Valeev Avatar answered Oct 05 '22 17:10

Tagir Valeev


Here are three different solutions to this problem. Each solution filters empty strings first as otherwise StringIndexOutOfBoundsException may be thrown.

This solution is the same as the solution from Tagir with the added code for filtering empty strings. I included it here primarily to compare to the other two solutions I have provided.

List<String> list =
    Arrays.asList("the", "", "quick", "", "brown", "", "fox");
StringBuilder builder = list.stream()
    .filter(s -> !s.isEmpty())
    .mapToInt(s -> s.codePointAt(0))
    .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append);
String result = builder.toString();
Assert.assertEquals("tqbf", result);

The second solution uses Eclipse Collections, and leverages a relatively new container type called CodePointAdapter that was added in version 7.0.

MutableList<String> list =
    Lists.mutable.with("the", "", "quick", "", "brown", "", "fox");
LazyIntIterable iterable = list.asLazy()
    .reject(String::isEmpty)
    .collectInt(s -> s.codePointAt(0));
String result = CodePointAdapter.from(iterable).toString();
Assert.assertEquals("tqbf", result);

The third solution uses Eclipse Collections again, but with injectInto and StringBuilder instead of CodePointAdapter.

MutableList<String> list =
    Lists.mutable.with("the", "", "quick", "", "brown", "", "fox");
StringBuilder builder = list.asLazy()
    .reject(String::isEmpty)
    .collectInt(s -> s.codePointAt(0))
    .injectInto(new StringBuilder(), StringBuilder::appendCodePoint);
String result = builder.toString();
Assert.assertEquals("tqbf", result);

Note: I am a committer for Eclipse Collections.

like image 36
Donald Raab Avatar answered Oct 03 '22 17:10

Donald Raab


Simple way using method reference :

List<String> list = Arrays.asList("ABC", "CDE");
StringBuilder sb = new StringBuilder();

list.forEach(sb::append);

String concatString = sb.toString();
like image 38
Rahul Chauhan Avatar answered Oct 03 '22 17:10

Rahul Chauhan