13

I have a single String of format:

row1col1 row1col2
row2col1 row2col2
row3col1 row3col2

and so on...

I want to extract each item and build an array of objects with properties like this:

new MyObject(row1col1, row1col2); 

I am new to Java 8 and Streams and I would like to find out how can I achieve this without loops.

Normally I would use a String.split('\n') for accumulating the rows into an array of String

And then a loop where for each line I would split again on the space separator and with the resulting array of the two elements (row1col1 row1col2) build my object, until there are no more rows to process.

Like this:

String sausage = "row1col1 row1col2\nrow2col1 row2col2\nrow3col1 row3col2";
String[] rows = sausage.split("\n");

for (String row : rows) {
    String[] objectData = u.split("\\s+");
    MyObject myObject = new MyObject(objectData[0], objectData[1]);
    myObjectList.add(myObject);
}

Can anyone explain me how to achieve the same with streams and what is the mechanism behind that allows me to do so?

Is this even a valid way of thinking when increasing the number of elements because from all the examples I've seen the streams focus on filtering, collecting or generally given a set of elements retrieve a minor set applying some criterias.

Kevin Cruijssen
  • 9,153
  • 9
  • 61
  • 135
Lucian Enache
  • 2,510
  • 5
  • 34
  • 59
  • 2
    Do you want the entire stream be in memory as an array, or do you wish to process one rowset at a time, and repeat the processing loop one row at a time? – Tschallacka Dec 11 '15 at 08:32
  • I actually load the stream from a file, so I think that it is read all in memory before being processed right ? – Lucian Enache Dec 11 '15 at 08:37
  • @LucianEnache, not necessarily. Depends how you're reading. – shmosel Dec 11 '15 at 08:38
  • 1
    Yes, but the difference is, do you want to load all string to memory(say it's 2mb), then split it in arrays(another 4mb) then iterate the arrays and turn them in your objects(another xmb) you end up with a lot of junk memory. Or you skip all that and process it all directly to your own objects, allowing for quick garbage cleanup and less risks of out of memory Thats why I asked. – Tschallacka Dec 11 '15 at 08:39
  • 2
    @MichaelDibbets Now I understand, technically speaking you would wish to process as you're loading, so I suppose that the preferd way is to use an inputStream and read along. – Lucian Enache Dec 11 '15 at 08:41

2 Answers2

11

A simple way would be to create a Pattern with the line separator and split the input String as a Stream. Then, each line is split with a space (keeping only 2 parts) and mapped to a MyObject. Finally, an array is constructed with the result.

public static void main(String[] args) {
    String str = "row1col1 row2col2\r\nrow2col1 row2col2\r\nrow3col1 row3col2";

    MyObject[] array =
        Pattern.compile(System.lineSeparator(), Pattern.LITERAL)
               .splitAsStream(str)
               .map(s -> s.split("\\s+", 2))
               .map(a -> new MyObject(a[0], a[1]))
               .toArray(MyObject[]::new);

    System.out.println(Arrays.toString(array));
}

Using splitAsStream can be advantageous over Stream.of(...) if the input String is long.

I assumed in the code that the line separator of the String was the default line separator (System.lineSeparator()) of the OS but you can change that if it is not.


Instead, if you are reading from a file, you could use Files.lines() to get a hold of a Stream of all the lines in the file:

MyObject[] array = Files.lines(path)
                        .map(s -> s.split("\\s+", 2))
                        .map(a -> new MyObject(a[0], a[1]))
                        .toArray(MyObject[]::new);

System.out.println(Arrays.toString(array));
Tunaki
  • 132,869
  • 46
  • 340
  • 423
7

You could generate a Stream of Strings that represent a single MyObject instance, and transform each of them to your MyObject instance (by first splitting them again and then constructing a MyObject instance) :

List<MyObject> list = 
   Stream.of(inputString.split("\n"))
      .map (s -> s.split(" "))
      .filter (arr -> arr.length == 2) // this validation may not be necessary
                                       // if you are sure each line contains 2 tokens
      .map (arr -> new MyObject(arr[0],arr[1]))
      .collect(Collectors.toList());
Eran
  • 387,369
  • 54
  • 702
  • 768