Преобразование списка в отображение с использованием потоков Java - PullRequest
0 голосов
/ 04 июня 2018

В моем коде повторяется следующий шаблон:

class X<T, V>
{
    V doTransform(T t) {
        return null; // dummy implementation
    }

    Map<T, V> transform(List<T> item) {
        return item.stream().map(x->new AbstractMap.SimpleEntry<>(x, doTransform(x))).collect(toMap(x->x.getKey(), x->x.getValue()));
    }
}

Требование использования AbstractMap.SimpleEntry является грязным и неуклюжим.Linqs использует анонимные типы более элегантно.

Есть ли более простой способ добиться этого с помощью потоков?

Заранее спасибо.

Ответы [ 3 ]

0 голосов
/ 04 июня 2018

К сожалению, Java не имеет точного эквивалента анонимных типов C #.

В этом конкретном случае вам не нужна промежуточная операция map, как предложил @Jorn Vernee.вместо этого вы можете выполнить извлечение ключей и значений в toMap сборщике.

Однако, когда дело доходит до случаев, когда вы думаете, что вам нужно что-то вроде анонимных типов C #, вы можете рассмотреть:

  1. анонимные объекты (может не всегда быть тем, что вы хотите в зависимости от вашего варианта использования)
  2. Arrays.asList(...), List.of(...) (может не всегда быть тем, что вы хотите в зависимости отваш вариант использования)
  3. массив (может не всегда быть тем, что вы хотите, в зависимости от вашего варианта использования)

В конечном счете, если вам действительно необходимо сопоставить счто-то, что может содержать два разных типа элементов, тогда я бы придерживался AbstractMap.SimpleEntry.

Это, как сказал ваш текущий пример, может быть упрощено до:

Map<T, V> transform(List<T> items) {
    return items.stream().collect(toMap(Function.identity(),this::doTransform));
}
0 голосов
/ 04 июня 2018

В этом конкретном примере вообще нет необходимости создавать промежуточное хранилище:

Map<T, V> transform(List<T> item) {
    return item.stream().collect(toMap(x -> x, x -> doTransform(x)));
}

Но если вам это нужно, Java 9 предлагает более простой фабричный метод,

Map<T, V> transform(List<T> item) {
    return item.stream()
               .map(x -> Map.entry(x, doTransform(x)))
               .collect(toMap(x -> x.getKey(), x -> x.getValue()));
}

до тех пор, пока вам не нужно иметь дело с null.

Здесь вы можете использовать анонимный внутренний класс,

Map<T, V> transform(List<T> item) {
    return item.stream()
               .map(x -> new Object(){ T t = x; V v = doTransform(x); })
               .collect(toMap(x -> x.t, x -> x.v));
}

, но он менее эффективен.Это внутренний класс, который захватывает ссылку на окружающий this, также он захватывает x, поэтому у вас есть два поля, t и синтетическое для захвата x, для одной и той же вещи.

Последний можно обойти с помощью метода, например

Map<T, V> transform(List<T> item) {
    return item.stream()
               .map(x -> new Object(){ T getKey() { return x; } V v = doTransform(x); })
               .collect(toMap(x -> x.getKey(), x -> x.v));
}

Но это не добавляет читабельности.

Единственными истинными анонимными типами являются типы, сгенерированные для лямбда-выражений,который может быть использован для хранения информации с помощью функций более высокого порядка:

Map<T, V> transform(List<T> item) {
    return item.stream()
               .map(x -> capture(x, doTransform(x)))
               .collect(HashMap::new, (m,f) -> f.accept(m::put), HashMap::putAll);
}
public static <A,B> Consumer<BiConsumer<A,B>> capture(A a, B b) {
    return f -> f.accept(a, b);
}

, но вы скоро столкнетесь с ограничениями системы типов Java (она по-прежнему не является функциональным языком программирования), если вы попытаетесьэто с более сложными сценариями.

0 голосов
/ 04 июня 2018

Вы можете вызвать doTransform в преобразователе значений:

Map<T, V> transform(List<T> item) {
    return item.stream().collect(toMap(x -> x, x -> doTransform(x)));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...