9

I am fairly new to streams, so please help me out (and be gentle).

What I would like to do is the following. I have a BufferedReader that reads from a file in which each line looks something like this: "a, b". For example:

Example Input file

"a, b"

"d, e"

"f, g"

I would like to convert this to a LinkedList<String[]>:

Example LinkedList<String[]>

[{"a", "b"}, {"c", "d"}, {"f", "g"}]

How would you do this using a stream approach?

This is what I tried:

List numbers = reader.lines().map(s -> s.split("[\\W]")).collect(Collectors.toList());

This does not work. My IDE provides the following feedback:

Incompatible types. Required List but 'collect' was inferred to R: no instance(s) of type variable(s) T exist so that List<T> conforms to List

It shows... I am still trying to figure streams out.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
MWB
  • 1,830
  • 1
  • 17
  • 38
  • 2
    Start by not using raw types. What should the List contains? If you didn't use rwa types, your code would be typesafe, and we would have that critical information. Also, why would you use a LinkedList? In 99;99% of the cases, an ArrayList is a better choice than a LinkedList. – JB Nizet Jan 03 '19 at 15:28
  • @JBNizet: Do you mean use `LinkedList numbers = ...` instead of `List numbers = ...`? I have tried that as well, didn't make a difference. – MWB Jan 03 '19 at 15:29
  • `[{"a", "b"}, {"c", "d"}, {"f", "g"}]` what is the type of `{"a", "b"}`? `Map`? `Map.Entry`? – scigs Jan 03 '19 at 15:33
  • 2
    No. I mean just List instead of the raw List. So you want a list of Strings? What should each String of the list contain? Read https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it – JB Nizet Jan 03 '19 at 15:33

3 Answers3

7

Firstly, I'd suggest avoiding the use of raw types and instead, use List<String[]> as the receiver type.

List<String[]> numbers = reader.lines()
                               .map(s -> s.split(delimiter)) // substitute with your deilimeter
                               .collect(Collectors.toList());

You mentioned that you wanted a LinkedList implementation. you should almost always favour ArrayList which toList returns by default currently although it is not guaranteed to persist thus, you can specify the list implementation explcitly with toCollection:

List<String[]> numbers = reader.lines()
                               .map(s -> s.split(delimiter)) // substitute with your deilimeter
                               .collect(Collectors.toCollection(ArrayList::new));

and likewise for LinkedList:

List<String[]> numbers = reader.lines()
                               .map(s -> s.split(delimiter)) // substitute with your deilimeter
                               .collect(Collectors.toCollection(LinkedList::new));
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
4

You may do it like so,

Path path = Paths.get("src/main/resources", "data.txt");
try (Stream<String> lines = Files.lines(path)) {
    List<String> strings = lines.flatMap(l -> Arrays.stream(l.split(","))).map(String::trim)
        .collect(Collectors.toCollection(LinkedList::new));
}

Read each line of the file, then split it using the delimiter. After that trim it to eliminate any remaining white space characters. Finally collect it to a result container.

Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
  • 2
    Would prefer this over the current answer IMHO. (+1) – Naman Jan 03 '19 at 16:26
  • 1
    @nullpointer But this code yields a `List` instead of a `List`... – fps Jan 03 '19 at 18:11
  • 1
    @FedericoPeraltaSchaffner that's the reason actually I would prefer this. Maybe I just find it difficult to see List of an array as one. – Naman Jan 04 '19 at 00:36
4

Supposing that each line is a tuple of 2 elements you could collect them into a List of something that looks like a tuple of 2 elements. Note that Java doesn't have native type for tuples (like Scala or python), so you should choose a way to represent the elements.

you could create a list of entry :

List<Map.Entry<String, String>> numbers = 
                 reader.lines()
                       .map(s -> s.split(","))
                       .map(a -> new AbstractMap.SimpleEntry<>(a[0], a[1]))
                       .collect(Collectors.toList());

or a list of String :

List<String> numbers = reader.lines()
                             .map(s -> s.split(","))
                             .map(a -> "{" + a[0] + "," + a[1] + "}"))
                             .collect(Collectors.toList());

Note that generally, you don't want to stick to a specific list implementation as you collect the stream while in some cases you can need. In this case, specify the collection supplier to use with toCollection(LinkedList::new) instead of toList()

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • The first approach is another nice way to store two related items +1. Although, the second approach would be difficult to deal with you if you later want to access each individual item and do something with it. – Ousmane D. Jan 03 '19 at 15:49
  • 2
    Agreed for the second. It sounds more usable as a data format. Not easy to answer smartly with a broad requirement :) EDIT : oh it was in the title "and generate a List of String-arrays?" My bad :) – davidxxx Jan 03 '19 at 15:53