How can I create a Stream with only one el

2019-04-18 06:55发布

问题:

Using Stream.of to create generic streams is very convenient, but what if I want to create a Stream<String[]> of only one element?

Let’s say I have:

String[] tropicalFruits = new String[] {"pineapple", "banana", "mango"};
String[] fruits = new String[] {"melon", "peach", "apple"};

Then Stream.of(tropicalFruits, fruits) produces a Stream<String[]> of two elements. How can I achieve the same for a stream of a single element? If I try:

Stream<String[]> fruittyStream = Stream.of(tropicalFruits);

I get:

Error: incompatible types: inference variable T has incompatible bounds
equality constraints: java.lang.String[]
lower bounds: java.lang.String

Stream<String[]> fruittyStream = Stream.of(fruits);
                                 ^---------------^

I’ve googled for this and searched in SO but I got nothing. It seems to me that it’s not a very unusual or esoeteric problem, so it’s kind of surprising I didn’t get any answers (or I’m not searching with the right keywords).

回答1:

Solution

Stream<String[]> stream = Stream.<String[]>of(tropicalFruits);

or

Stream<String[]> stream = Stream.of(new String[][]{tropicalFruits});

Explanation

To produce a Stream<T>, Stream.of takes either T or T....
A T[] parameter perfectly applies to the second signature.
Therefore, passing a String[] invokes the Stream.of(String...) version.

To change this behaviour, we need to provide some extra information about T (1) or define it more clearly (=unambiguously) (2).

There are two ideas came to my mind:

  1. To specify a type argument of the method explicitly to use the first signature.
    Stream.<String[]>of(new String[]{}) will produce a Stream<String[]>.
  2. To wrap a T[] value in a T[][] array to use the second signature.
    Stream.of(new String[][]{}) will produce a Stream<String[]>.


回答2:

This Stream<String[]> fruittyStream = Stream.of(tropicalFruits);

calls the var-arg method of Stream.of.

I can think of this workaround:

List<String> list = Arrays.asList("one");
Stream.of(list)
      .map(x -> x.toArray(new String[1]));

Or you can call the var-args method a bit differently:

 Stream.of(tropicalFruits, null)
       .filter(Objects::nonNull)
       .forEach(x -> System.out.println(Arrays.toString(x)));


回答3:

By calling Stream#of with a single T[], Java defaults to the vararg factory method, creating a Stream<T> rather than a Stream<T[]>. To create a Stream<T[]> with a single element, you can either create a Stream<T[]> with multiple elements and call limit(1), or use a dummy array for the second element:

Stream<String[]> stream = Stream.of(tropicalFruits, fruits).limit(1);

Stream<String[]> stream = Stream.of(tropicalFruits, new String[] {});