Соберите список Long from Double stream в Java 8

У меня есть следующий код:

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());

Этот код не работает, и ошибка компиляции:

Метод collect в интерфейсе java.util.stream.LongStream не может быть применен к данным типам;
обязательно: java.util.function.Supplier<R>,java.util.function.ObjLongConsumer<R>,java.util.function.BiConsumer<R,R>
найдено: java.util.stream.Collector<java.lang.Object,capture#1 of?,java.util.List<java.lang.Object>>
причина: не может вывести переменную типа (типов) R (фактические и формальные списки аргументов различаются по длине)

Я испробовал много способов использования Коллекционеров, но я до сих пор не могу заставить его работать. Что я делаю неправильно?

Ответ 1

mapToLong дает вам LongStream, который не может быть collect -ed на Collectors.toList.

Это потому, что LongStream

Последовательность примитивных длиннозначных элементов

У нас не может быть List<long>, нам нужен List<Long>. Поэтому, чтобы иметь возможность собирать их, нам сначала нужно объединить эти long примитивы в Long объекты:

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

Метод в boxed дает нам Stream<Long> который мы можем собрать в список.

Использование map вместо mapToLong, как предложил Луи Вассерман, также будет работать, потому что это приведет к появлению Steam<Long> котором значения будут автоматически упакованы:

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

Ответ 2

Это должно скомпилироваться, если вы используете map вместо mapToLong. (Я не уверен, что вы пытаетесь сделать с doubleToRawLongBits, имеет смысл, но это, по крайней мере, скомпилируется.)

Ответ 3

Не уверен, что вы ожидаете, что ваши результаты будут похожи, но это создает 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);
}

Ответ 4

Неясно, почему вы хотите использовать doubleToRawLongBits. Если ваша проблема заключается в том, что умножение с помощью 2.5 создает double, а не long, вам нужно преобразовать тип, чтобы преобразовать значение, поскольку doubleToRawLongBits не является каноническим способом преобразования double в long. Вместо этого этот метод возвращает представление IEEE 754 значения, которое интересно только в особых случаях. Обратите внимание, что вы можете выполнить преобразование прямо в первой операции map:

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());

Это даже применяется, если вы действительно хотите представление IEEE 754 значений double:

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

Но учтите, что если у вас есть временный список, тип которого соответствует типу результата, вы можете выполнить операцию на месте вместо создания двух списков (и пройти через API Stream):

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

то же самое, даже если вы хотите биты IEEE 754:

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

Если вы настаиваете на использовании API Stream, вы можете использовать построитель вместо ArrayList для исходных данных:

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);

Ответ 5

Суть этой проблемы в том, что возвращаемым значением функции mapToLong является интерфейс LongStream. LongStream имеет только метод

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

Вы можете хотеть использовать метод

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

Вы можете найти этот метод в классе java.util.stream.Stream.

LongStream и Stream имеют расширенных отношений.

Ответ 6

    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());