Search code examples
javajava-8java-stream

Merge two text input files, alternating lines, using the Java stream API


I was trying to solve a problem using Java 8 that I have already solved using a simple for loop. However I have no idea how to do this.

The problem is:

File1:

1,sdfasfsf
2,sdfhfghrt
3,hdfxcgyjs

File2:

10,xhgdfgxgf
11,hcvcnhfjh
12,sdfgasasdfa
13,ghdhtfhdsdf

Output should be like

1,sdfasfsf
10,xhgdfgxgf
2,sdfhfghrt
11,hcvcnhfjh
3,hdfxcgyjs
12,sdfgasasdfa
13,ghdhtfhdsdf

I already have this basically working.

The core logic is:

List<String> left  = readFile(lhs);
List<String> right = readFile(rhs);
int leftSize = left.size();
int rightSize = right.size();
int size =  leftSize > rightSize? leftSize : right.size();
for (int i = 0; i < size; i++) {
    if(i < leftSize) {
        merged.add(left.get(i));
    }
    if(i < rightSize) {
        merged.add(right.get(i));
    }
}

I boasted I could do this easily using streams and now I am not sure if this can even be done.

How can this be solved using streams?


Solution

  • You may simplify your operation to have less conditionals per element:

    int leftSize = left.size(), rightSize = right.size(), min = Math.min(leftSize, rightSize);
    List<String> merged = new ArrayList<>(leftSize+rightSize);
    for(int i = 0; i < min; i++) {
        merged.add(left.get(i));
        merged.add(right.get(i));
    }
    if(leftSize!=rightSize) {
        merged.addAll(
            (leftSize<rightSize? right: left).subList(min, Math.max(leftSize, rightSize)));
    }
    

    Then, you may replace the first part by a stream operation:

    int leftSize = left.size(), rightSize = right.size(), min = Math.min(leftSize, rightSize);
    
    List<String> merged=IntStream.range(0, min)
            .mapToObj(i -> Stream.of(left.get(i), right.get(i)))
            .flatMap(Function.identity())
            .collect(Collectors.toCollection(ArrayList::new));
    if(leftSize!=rightSize) {
        merged.addAll(
            (leftSize<rightSize? right: left).subList(min, Math.max(leftSize, rightSize)));
    }
    

    But it isn’t really simpler than the loop variant. The loop variant may be even more efficient due to its presized list.

    Incorporating both operation into one stream operation would be even more complicated (and probably even less efficient).