aldrael
aldrael

Reputation: 567

Collect list of Long from Double stream in Java 8

I have the following code:

List<Long> list = new ArrayList<>();
list.add(4L);
list.add(92L);
list.add(100L);
List<Long> newList = list.stream().map(i -> i * 2.5)
                                  .mapToLong(Double::doubleToRawLongBits)
                                  .collect(Collectors.toList());

This code doesn't work and the compilation error is:

method collect in interface java.util.stream.LongStream cannot be applied to given types;
required: java.util.function.Supplier<R>,java.util.function.ObjLongConsumer<R>,java.util.function.BiConsumer<R,R>
found: java.util.stream.Collector<java.lang.Object,capture#1 of ?,java.util.List<java.lang.Object>>
reason: cannot infer type-variable(s) R (actual and formal argument lists differ in length)

I have tried many usages of Collectors but I still can't make it to work. What am I doing wrong?

Upvotes: 41

Views: 61197

Answers (6)

Michael
Michael

Reputation: 44110

mapToLong gives you a LongStream which is not able to be collect-ed by Collectors.toList.

This is because LongStream is

A sequence of primitive long-valued elements

We can't have a List<long>, we need a List<Long>. Therefore to be able to collect them we first need to box these primitive longs into Long objects:

list.stream().map(i -> i * 2.5)
    .mapToLong(Double::doubleToRawLongBits)
    .boxed()                                //< I added this line
    .collect(Collectors.toList());

The boxed method gives us a Stream<Long> which we're able to collect to a list.

Using map rather than mapToLong will also work because that will result in a Steam<Long> where the values are automatically boxed:

list.stream().map(i -> i * 2.5)
    .map(Double::doubleToRawLongBits)
    .collect(Collectors.toList());

Upvotes: 107

JavaMan
JavaMan

Reputation: 1217

    HashMap<String, Map<String, Long>> map = new HashMap<>();

    List<Entry<String, Map<String, Long>>> sortedList = map
    .entrySet()
    .stream()
    .sorted((a, b) -> Long.compare(
                                   a.getValue().values().stream().mapToLong(l -> l).sum(),
                                   b.getValue().values().stream().mapToLong(l -> l).sum()))

    .collect(Collectors.toList());

Upvotes: 0

Monet
Monet

Reputation: 11

The essence of this problem is that the return value of function mapToLong is LongStream interface. LongStream only has method

 <R> R collect(Supplier<R> supplier,
               ObjLongConsumer<R> accumulator,
               BiConsumer<R, R> combiner);

You may be want to use method

<R, A> R collect(Collector<? super T, A, R> collector);

You can find this method in java.util.stream.Stream class.

the LongStream and Stream have no extend relationship.

Upvotes: 1

Holger
Holger

Reputation: 298123

It’s not clear why you want to use doubleToRawLongBits. If your problem is that the multiplication with 2.5 produces double rather than long, you need a type cast to convert the value, as doubleToRawLongBits is not the canonical way of converting double to long. Instead, this method returns the IEEE 754 representation of the value which is only interesting in very special cases. Note that you can perform the conversion right inside the first map operation:

List<Long> list = new ArrayList<>();
list.add(4L);
list.add(92L);
list.add(100L);

List<Long> newList = list.stream().map(i -> (long)(i * 2.5))
                         .collect(Collectors.toList());

This even applies if you really want the IEEE 754 representation of double values:

List<Long> newList = list.stream().map(i -> Double.doubleToRawLongBits(i * 2.5))
                         .collect(Collectors.toList());

But note that if you have a temporary list whose type matching the result type, you may perform the operation in-place instead of creating two lists (and going through the Stream API):

List<Long> list = new ArrayList<>();
list.add(4L);
list.add(92L);
list.add(100L);
list.replaceAll(i -> (long)(i * 2.5));

again, the same applies even if you want IEEE 754 bits:

List<Long> list = new ArrayList<>();
list.add(4L);
list.add(92L);
list.add(100L);
list.replaceAll(i -> Double.doubleToRawLongBits(i * 2.5));

If you insist on using the Stream API, you may use the builder rather than an ArrayList for the source data:

Stream.Builder<Long> b = Stream.builder();
b.add(4L);
b.add(92L);
b.add(100L);
List<Long> newList = b.build().map(i -> (long)(i * 2.5))
                      .collect(Collectors.toList());
newList.forEach(System.out::println);

Upvotes: 1

OldCurmudgeon
OldCurmudgeon

Reputation: 65803

Not sure what you expect your results to look like but this generates a List<Long>.

public void test() {
    List<Long> list = new ArrayList<>();
    list.add(4L);
    list.add(92L);
    list.add(100L);
    List<Long> newList = list.stream()
            // Times 1.5.
            .map(i -> i * 2.5)
            // Grab the long bits.
            .mapToLong(Double::doubleToRawLongBits)
            // Box them.
            .boxed()
            // Make a list.
            .collect(Collectors.toList());
    System.out.println(newList);
}

Upvotes: 6

Louis Wasserman
Louis Wasserman

Reputation: 198023

This should compile if you use map instead of mapToLong. (I'm not sure what you are trying to do with doubleToRawLongBits makes any sense, but that will at least compile.)

Upvotes: 17

Related Questions