Joe
Joe

Reputation: 326

Convert an loop (while and for) to stream

I have started working with Java 8 and trying to convert some loops and old syntax in my code to lambdas and streams.

So for example, I'm trying to convert this while and for loop to stream, but I'm not getting it right:

List<String> list = new ArrayList<>();
if (!oldList.isEmpty()) {// old is a List<String>
    Iterator<String> itr = oldList.iterator();
    while (itr.hasNext()) {
        String line = (String) itr.next();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (line.startsWith(entry.getKey())) {
         String newline = line.replace(entry.getKey(),entry.getValue());
                list.add(newline);
            }
        }
    }
}

I wanted to know if it's possible to convert the above example to a single stream where there is a while loop inside of a for loop.

Upvotes: 15

Views: 29149

Answers (4)

Bohemian
Bohemian

Reputation: 425013

To answer your question, it's a 1-liner:

List<String> list = oldList.stream()
    .filter(line -> map.keySet().stream().anyMatch(line::startsWith))
    .map(line -> map.entrySet().stream()
        .filter(entry -> line.startsWith(entry.getKey()))
        .map(entry -> line.replace(entry.getKey(), entry.getValue()))
        .findFirst().get())
    .collect(Collectors.toList());

Upvotes: 4

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

You can achieve it with a stream nested in a stream created from oldList list. Nested stream plays role of mapping current value from oldList with a mapper defined in map, e.g.

public static void main(String[] args) {
    final List<String> oldList = Arrays.asList("asd-qwe", "zxc", "123");
    final Map<String, String> map = new HashMap<String, String>() {{
        put("asd", "zcx");
        put("12", "09");
        put("qq", "aa");
    }};

    List<String> result = oldList.stream()
            .map(line -> map.entrySet()
                    .stream()
                    .filter(entry -> line.startsWith(entry.getKey()))
                    .map(entry -> line.replace(entry.getKey(), entry.getValue()))
                    .collect(Collectors.toList())
            )
            .flatMap(Collection::stream)
            .collect(Collectors.toList());


    System.out.println(result);
}

Following example produces output like:

[zcx-qwe, 093]

Suggested solution can be easily parallelized if needed. Functional approach with no side effects.

Upvotes: 1

Chris
Chris

Reputation: 23171

As was stated above, using streams here doesn't really add value since it makes the code harder to read/understand. I get that you're doing it more as a learning exercise. That being said, doing something like this is a slightly more functional-style approach as it doesn't have a side effect of adding to the list from within the stream itself:

list = oldList.stream().flatMap(line->
            map.entrySet().stream()
                    .filter(e->line.startsWith(e.getKey()))
                    .map(filteredEntry->line.replace(filteredEntry.getKey(),filteredEntry.getValue()))
        ).collect(Collectors.toList());

Upvotes: 11

Robin Topper
Robin Topper

Reputation: 2344

I don't see why you would want to use streams here, but it is possible.

Create some test input:

List<String> oldList = Arrays.asList("adda","bddb","cddc");
Map<String,String> map = new HashMap<>();
map.put("a", "x");
map.put("b", "y");
map.put("c", "z");

List<String> list = new ArrayList<>();

The actual code:

oldList.stream()
    .forEach(line -> map.entrySet().stream()
            .filter(entry -> line.startsWith(entry.getKey()))
            .forEach(entry -> list.add(line.replace(entry.getKey(),entry.getValue()))));

Print the outcome:

list.forEach(System.out::println);

Which is:

xddx
yddy
zddz

Upvotes: 5

Related Questions