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.

4
  • 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. Commented Jan 3, 2019 at 15:28
  • @JBNizet: Do you mean use LinkedList<String> numbers = ... instead of List numbers = ...? I have tried that as well, didn't make a difference. Commented Jan 3, 2019 at 15:29
  • [{"a", "b"}, {"c", "d"}, {"f", "g"}] what is the type of {"a", "b"}? Map<String, String>? Map.Entry<String, String>? Commented Jan 3, 2019 at 15:33
  • 2
    No. I mean just List<String> instead of the raw List. So you want a list of Strings? What should each String of the list contain? Read stackoverflow.com/questions/2770321/… Commented Jan 3, 2019 at 15:33

3 Answers 3

8

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));
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks that helped a lot!
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.

3 Comments

Would prefer this over the current answer IMHO. (+1)
@nullpointer But this code yields a List<String> instead of a List<String[]>...
@FedericoPeraltaSchaffner that's the reason actually I would prefer this. Maybe I just find it difficult to see List of an array as one.
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()

2 Comments

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.
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 :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.