14

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.

Szymon Stepniak
  • 40,216
  • 10
  • 104
  • 131
Joe
  • 296
  • 1
  • 7
  • 19
  • 3
    "trying to convert some loops and old syntax in my code to lambdas and streams." what are you trying to gain here? Streams can simplify some tasks but sometimes loops are easier to read and maintain. Also why do you have `Iterator itr = oldList.iterator(); while (itr.hasNext()) { String line = (String) itr.next(); ... }` instead of `for (String line : oldList){...}`? You can also remove `if (!oldList.isEmpty())` since if list is empty you would not iterate. – Pshemo Aug 22 '17 at 15:03
  • Hi Pshemo, thank you for such quick response. Its an already existing code so I m just trying to see if I can update some of the loops which are there to Streams. I did some already without errors but just was curious if it can be done when there are nested loops like above. – Joe Aug 22 '17 at 15:07
  • 2
    This is essentially asking "how to replace nested loops with streams": https://stackoverflow.com/q/27174700/1531971 –  Aug 22 '17 at 15:11

4 Answers4

11

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());
Chris
  • 22,923
  • 4
  • 56
  • 50
5

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
Robin Topper
  • 2,295
  • 1
  • 17
  • 25
4

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());
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • @Pshemo nah - it needed `.findFirst().get()` to convert the stream to a String. And this compiles :) – Bohemian Aug 22 '17 at 15:52
1

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.

Szymon Stepniak
  • 40,216
  • 10
  • 104
  • 131