9

I have a text file like this:

ids.txt

1000
999
745
123
...

I want to read this file and load it in a two dimensional array. I expect to have an array similar to the one below:

Object[][] data = new Object[][] { //
     { new Integer(1000) }, //
     { new Integer(999) }, //
     { new Integer(745) }, //
     { new Integer(123) }, //
     ...
};

Here is the code I wrote:

File idsFile = ... ;
try (Stream<String> idsStream = Files.lines(idsFile.toPath(), StandardCharsets.US_ASCII)) {
    Object[][] ids = idsStream
       .filter(s -> s.trim().length() > 0)
       .toArray(size -> new Object[size][]);

    // Process ids array here...
}

When running this code, an exception is raised:

java.lang.ArrayStoreException: null
at java.lang.System.arraycopy(Native Method) ~[na:1.8.0_45]
at java.util.stream.SpinedBuffer.copyInto(Unknown Source) ~[na:1.8.0_45]
at java.util.stream.Nodes$SpinedNodeBuilder.copyInto(Unknown Source) ~[na:1.8.0_45]
at java.util.stream.SpinedBuffer.asArray(Unknown Source) ~[na:1.8.0_45]
at java.util.stream.Nodes$SpinedNodeBuilder.asArray(Unknown Source) ~[na:1.8.0_45]
at java.util.stream.ReferencePipeline.toArray(Unknown Source) ~[na:1.8.0_45]
... 

How can resolve this exception?

2
  • Why new Integer(1000)? And you want an [][] where the inner array always has length == 1? Commented Jun 13, 2015 at 10:08
  • @BoristheSpider The ids are passed to a third party library that accept only Object[][] as input. Commented Jun 13, 2015 at 10:09

2 Answers 2

16

Given a Stream<String> you can parse each item to an int and wrap it into an Object[] using:

 strings
        .filter(s -> s.trim().length() > 0)
        .map(Integer::parseInt)
        .map(i -> new Object[]{i})

Now to turn that result into a Object[][] you can simply do:

Object[][] result = strings
        .filter(s -> s.trim().length() > 0)
        .map(Integer::parseInt)
        .map(i -> new Object[]{i})
        .toArray(Object[][]::new);

For the input:

final Stream<String> strings = Stream.of("1000", "999", "745", "123");

Output:

[[1000], [999], [745], [123]]
Sign up to request clarification or add additional context in comments.

5 Comments

If you suspect that strings may sporadically have spaces before and after, then it's better to add the separate trim step: strings.map(String::trim).filter(s -> !s.isEmpty()). Also I would use Integer::valueOf instead of Integer::parseInt as it matches better the wanted type (Integer, not int). Though probably that's a matter of taste.
@TagirValeev Why is it "better to add the separate trim step" ?
@Stephan: this way you can parse files which have spaces after numbers (like "1 \n2\n3 \n"). Currently you will have NumberFormatException on such files.
Do you think there is a benefit in these two map operations instead of simply saying .map(s -> new Object[]{Integer.parseInt(s)})?
@Holger don't know really. I like to split each logical transformation into a separate map step - I think it makes to code more readable. I suppose it's just a matter of personal preference - I doubt there's much of a performance impact.
5

Your last line should probably be size -> new Object[size], but you would need to provide arrays of Integers of size one and you would also need to parse the strings into Integers.

I suggest the following:

try (Stream<String> idsStream = Files.lines(idsFile.toPath(), StandardCharsets.US_ASCII)) {
    Object[][] ids = idsStream
       .map(String::trim)
       .filter(s -> !s.isEmpty())
       .map(Integer::valueOf)
       .map(i -> new Integer[] { i })
       .toArray(Object[][]::new);

    // Process ids array here...
}

Comments

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.