Text file manipulation with Java 8 and Guava I am trying to do some translation of text files. I have a translation .csv file and a file I need to translate (tab delimited). The translation is creating a file with an extra column of the translation.

I'd like to hear any suggestions on this.

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class TextFileManipulationExample {

  public static void main(String[] args) {
    new TextFileManipulationExample().translate();
  }

  private void translate() {
    List&lt;String&gt; translationFileContent = readFile("dictionary.csv");
    Stream&lt;List&lt;String&gt;&gt; translationLines = translationFileContent.stream().map(
      line -&gt; Splitter.on(",").splitToList(line));
    Map&lt;List&lt;String&gt;, String&gt; translationMap = translationLines.collect(Collectors.toMap(l -&gt; Lists.newArrayList(l.subList(0, 2)), l -&gt; l.get(2)));
    List&lt;String&gt; toTranslateLines = readFile("file_to_translate.txt");
    Stream&lt;List&lt;String&gt;&gt; listWithTranslation = toTranslateLines.stream().map(line -&gt; {
      List&lt;String&gt; list = Lists.newArrayList(Splitter.on("\t").splitToList(line));
      if (translationMap.containsKey(list)) {
        list.add(translationMap.get(list));
      }
      return list;
    });
    String contentToWrite = listWithTranslation.map(lineList -&gt; Joiner.on(",").join(lineList)).collect(
      Collectors.toList()).stream().reduce((t, u) -&gt; t + "\n" + u).get();
    writeFile(contentToWrite, "translated.csv");
  }

  private void writeFile(String contentToWrite, String fileName) {
    try {
      Files.write(contentToWrite, new File(fileName), Charset.defaultCharset());
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  private List&lt;String&gt; readFile(String fileName) {
    try {
      return Files.readLines(new File(fileName), Charset.defaultCharset());
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
}
You are using Java 8's functional stream programming, but you leave out method chaining. Depending on how you chain methods, it could be more or it could be less readable. You might be imagining this:

readFile("dictionary.csv").stream().map(line -&gt; Splitter.on(",").splitToList(line)).collect(Collectors.toMap(l -&gt; Lists.newArrayList(l.subList(0, 2)), l -&gt; l.get(2)));


There is no way I could read that. However, consider this:

Map&lt;List&lt;String&gt;, String&gt; translationMap = readFile("dictionary.csv").stream()
            .map(line -&gt; Splitter.on(",").splitToList(line))
            .collect(
                Collectors.toMap(
                    l -&gt; Lists.newArrayList(l.subList(0, 2)),
                    l -&gt; l.get(2)
                )
            );


I find this much more readable. By simply reading vertically, I can determine that the code does this:


Reads dictionary.csv into a list of strings (via readFile).
Split each string in the dictionary on ",", into a List&lt;String&gt;.
Collect the List&lt;String&gt;s into a Map, where


The keys are the first two elements of each list, still in a List
The values are the 3rd element of each list.



On the other hand, your code as is is littered with type names; do I really need to know that that is a Stream&lt;List&lt;String&gt;&gt; when that's easily determined from context?:

List&lt;String&gt; translationFileContent = readFile("dictionary.csv");
Stream&lt;List&lt;String&gt;&gt; translationLines = translationFileContent.stream().map(
  line -&gt; Splitter.on(",").splitToList(line));
Map&lt;List&lt;String&gt;, String&gt; translationMap = translationLines.collect(Collectors.toMap(l -&gt; Lists.newArrayList(l.subList(0, 2)), l -&gt; l.get(2)));




Alternatively, if you really like the temporary variables, put some empty lines in there to separate logical sections. Also, consider using Project Lombok, where you could just do this:

List&lt;String&gt; translationFileContent = readFile("dictionary.csv");
val translationLines = translationFileContent.stream().map(
    line -&gt; Splitter.on(",").splitToList(line));
val translationMap = translationLines.collect(Collectors.toMap(l -&gt; Lists.newArrayList(l.subList(0, 2)), l -&gt; l.get(2)));


(Coder discretion advised on where you should use val).