Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement counting variable with lambda expression java

I have a question about lambda expressions. I have a class Pair which should hold a String and an int.

Pair gets the String out of a file. and the int is representiv for the line number. So far I have this:

 Stream<String> lineNumbers = Files.lines(Paths.get(fileName));
    List<Integer> posStream = Stream.iterate(0, x -> x + 1).limit(lineNumbers.count()).collect(Collectors.toList());
    lineNumbers.close();
    Stream<String> line = Files.lines(Paths.get(fileName));
    List<Pair> pairs = line.map((f) -> new Pair<>(f,1))
            .collect(Collectors.toList());
    pairs.forEach(f -> System.out.println(f.toString()));
    line.close();

How can I now input the file numbers to the pairs? Is there a lambda expression which can perform this? Or do I need something else?

like image 200
Meant2Play Avatar asked Feb 11 '23 08:02

Meant2Play


1 Answers

There are a few ways to do this. The counter technique suggested by Saloparenator's answer could be implemented as follows, using an AtomicInteger as the mutable counter object and assuming the obvious Pair class:

List<Pair> getPairs1() throws IOException {
    AtomicInteger counter = new AtomicInteger(0);
    try (Stream<String> lines = Files.lines(Paths.get(FILENAME))) {
        return lines.parallel()
                    .map(line -> new Pair(line, counter.incrementAndGet()))
                    .collect(toList());
    }
}

The problem is that if the stream is run in parallel, the counter won't be incremented in the same order as the lines are read! This will occur if your file has several thousand lines. The Files.lines stream source will batch up bunches of lines and dispatch them to several threads, which will then number their batches in parallel, interleaving their calls to incrementAndGet(). Thus, the lines won't be numbered sequentially. It will work if you can guarantee that your stream will never run in parallel, but it's often a bad idea to write streams that are likely to return different results sequentially vs. in parallel.

Here's another approach. Since you're reading all the lines into memory no matter what, just read them all into a list. Then use a stream to number them:

static List<Pair> getPairs2() throws IOException {
    List<String> lines = Files.readAllLines(Paths.get(FILENAME));
    return IntStream.range(0, lines.size())
                    .parallel()
                    .mapToObj(i -> new Pair(lines.get(i), i+1))
                    .collect(toList());
}
like image 72
Stuart Marks Avatar answered Feb 13 '23 06:02

Stuart Marks