简体   繁体   中英

Java 8 streams and simple types

Can anyone explain to me why the following does not work:

long la[] = new long[] {1,2,3};
Arrays.stream(la).map(Long::valueOf).collect(Collectors.toSet());

When this does:

String la[] = new String[] {"1","2","3"};
Arrays.stream(la).map(Long::valueOf).collect(Collectors.toSet());

The former gives a compilation error while the latter does not. The compilation error is so cryptic (Eclipse) that I cannot make sense of it.

Arrays.stream(la) executes the method public static LongStream stream(long[] array) which produces a LongStream . LongStream 's map method returns a LongStream (ie each long element of the source LongStream is mapped to a long element in the target LongStream ). LongStream doesn't have a collect method that accepts a single argument, which is why collect(Collectors.toSet()) doesn't pass compilation.

It should work if you use mapToObj :

Set<Long> set = Arrays.stream(la).mapToObj(Long::valueOf).collect(Collectors.toSet());

Your second snippet works since here Arrays.stream produces a Stream of a reference type ( Stream<String> ) whose map method produces another Stream of a reference type ( Stream<Long> in your case). Here, Stream has a collect method that accepts a single argument - collect(Collector<? super T, A, R> collector) - so collect(Collectors.toSet()) works.

The code only looks the same. The method Arrays.stream that is being called is actually different in both cases:

On a Stream<String> , you can call map and return a Stream<R> based on the return type of the mapper. But on a LongStream , map will always return a LongStream , that is the primitive specialization. What happens is that Long::valueOf will turn your long element into a Long object and then it will be automatically unboxed into a long ; effectively, the call is doing nothing except a box / unbox.

Then the problem appears on the collect call.

  • LongStream.collect expects 3 arguments
  • Stream.collect has a 3 argument method but also a 1 argument method, which is the one you call with .collect(Collectors.toSet()); .

So you can't call .collect(Collectors.toSet()); on a LongStream . This won't compile: it expects 3 arguments.

What you can do is call mapToObj instead of map on the LongStream : this method declares to return a Stream<R> (instead of a LongStream ) from the return type of the mapper. In this case, the mapper is Long::valueOf that returns a Long object so it will return a Stream<Long> .

To recap:

  long la[] = new long[] {1,2,3};
  Arrays.stream(la).map(Long::valueOf).collect(Collectors.toSet());
//^--LongStream----^^---LongStream----^^     error

  String la[] = new String[] {"1","2","3"};
  Arrays.stream(la).map(Long::valueOf).collect(Collectors.toSet());
//^-Stream<String>-^^--Stream<Long>--^^---- successful call -----^

  long la[] = new long[] {1,2,3};
  Arrays.stream(la).mapToObj(Long::valueOf).collect(Collectors.toSet());
//^--LongStream----^^-----Stream<Long>-----^^---- successful call -----^

To answer why the first one doesn't compile, this creates a LongStream :

Arrays.stream(la)

This takes each long , creates a Long wrapper, then unboxes it back to long . Stream is still a LongStream :

.map(Long::valueOf)

This passes a single parameter to LongStream.collect , which fails because LongStream.collect required 3 parameters:

.collect(Collectors.toSet())

You need to change the map to mapToObj if you want to change the LongStream into a Stream<Long> (or call LongStream.boxed() )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM