3

Why doesn't the below code limit the output to first three chars only?

 String vowelOne = "aaebcd";

 Stream
    .of(vowelOne.toCharArray())
    .limit(3)       
    .forEach(System.out::println);

Output:

  aaebcd

I want the output to be:

  aae
Eran
  • 387,369
  • 54
  • 702
  • 768
Eager
  • 225
  • 1
  • 8
  • See also http://stackoverflow.com/questions/39874242/stream-vs-stream-of/39874305 and http://stackoverflow.com/questions/32471799/convert-a-string-to-a-java-util-streamcharacter and http://stackoverflow.com/questions/27888429/how-can-i-create-a-stream-from-an-array – Tunaki Nov 16 '16 at 15:04

3 Answers3

4

Stream.of(char[]) produces a Stream of a single char[] element (just like Arrays.asList() for a primitive array would produce a List of a single element), so limit(3) doesn't truncate the Stream, and you are printing the entire char[] (which you can also see from the fact that all the characters are printed in a single line even though you use println).

Try :

vowelOne.chars() // this returns an IntStream of the characters of the input String
        .limit(3)
        .forEach(i->System.out.println((char)i)); // without the casting to char, you'll
                                                  // get the numeric value of the first 3
                                                  // characters

Output :

a
a
e
Eran
  • 387,369
  • 54
  • 702
  • 768
1

Generally codePoints() instead of chars() is better and it handles surrogate pairs too. I have to admit this might be too much for your case in the question.

private static String firstN(String input, int limit) {
    StringBuilder builder = new StringBuilder();
    input.codePoints().limit(limit).forEach(builder::appendCodePoint);
    return builder.toString();
}
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 1
    That leads to the question what `limit` refers to. Even code points aren’t conceptual characters, i.e. you still might split right in the middle of a character consisting of multiple code points. By the way, the `.mapToObj(i -> i)` is obsolete, performing a boxing operation just to do an unboxing right in the next step. – Holger Nov 16 '16 at 14:21
  • 1
    @Holger indeed. I immediately thought about devanagari letters that have multiple code points. For the simple cases like French and Swedish letters, Normalization needs to be applied first, for the devanagari letters I have no idea to be honest. Yes that map was a bad copy/paste, thx. – Eugene Nov 16 '16 at 14:26
  • Note that you can make it a cleaner `forEach`less solution using `collect`, as shown [here](http://stackoverflow.com/a/20268845/2711488). – Holger Nov 16 '16 at 15:05
0

As Eran mentioned, Stream.of(char[]) will produce a Stream<char[]>, not a Stream<char> as you need it to be. There are additional methods to circumvent this. You either need an array of the boxed type Character[]

static Character[] toCharacterArray(String s) {
    Character[] array = new Character[s.length()];
    for(int i = 0; i < s.length(); i++) {
       array[i] = s.charAt(i);
    }
    return array;
}

and use it like this

Stream
    .of(toCharacterArray(vowelOne))
    .limit(3)
    .forEach(System.out::println);

Or you could use String.split("") to create a String[]

String vowelOne = "aaebcd";

Stream
    .of(vowelOne.split(""))
    .limit(3)
    .forEach(System.out::println);

Both methods result in

a
a
e
QBrute
  • 4,405
  • 6
  • 34
  • 40
  • you have just created an array of each character and saved it in String[] then you limited the number of arrays to. It would work but don't u think that you've just complicated it rather than simply entering a stream of chars, its messy tbh. – Ahmad Sanie Nov 16 '16 at 11:58
  • @AhmadAlsanie Where do I save each character in a `String[]`? – QBrute Nov 16 '16 at 12:05
  • vowelOne.split("") -> will create a String[a], String[a], String[e], String[b], String[c], String[d] – Ahmad Sanie Nov 16 '16 at 12:08
  • If you're going to create an additional array of Characters in order to have a `Stream`, you'd better of using `IntStream.range(0, arr.length).mapToObj(i -> arr[i]).moreStreamOps(....)` directly. – Alexis C. Nov 16 '16 at 12:20
  • Of course the other solutions are better. The point of this was only to *demonstrate* that the desired behaviour is not shown with primitive arrays but rather with the boxed version, i.e. `Character[]` instead of `char[]`. – QBrute Nov 16 '16 at 12:29
  • 1
    If you really want a `Stream`, using `string.chars().mapToObj(i -> Character.valueOf((char)i))` would be straight-forward… Note that the behavior is merely a matter of overload resolution, i.e. you can exhibit the same behavior with non-primitive array too by using `Stream. of(toCharacterArray(vowelOne)) .limit(3) .forEach(System.out::println);` but since there is no specialized `println(Character[])`, the not so expressive `toString()` output of the array type will be used. – Holger Nov 16 '16 at 14:14