2015-06-23 5 views
47

Я использую Java 8 lambdas и хочу использовать CollectorstoMap, чтобы вернуть SortedMap. Самое лучшее, что я могу придумать, - назвать следующее: CollectorstoMap метод с манекеном mergeFunction и mapSupplier равен TreeMap::new.Java 8 Collectors.toMap SortedMap

public static <T, K, U, M extends Map<K, U>> 
     Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, 
       Function<? super T, ? extends U> valueMapper, 
       BinaryOperator<U> mergeFunction, 
       Supplier<M> mapSupplier) { 
    BiConsumer<M, T> accumulator = (map, element) -> map.merge(keyMapper.apply(element), 
      valueMapper.apply(element), mergeFunction); 
    return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID); 
} 

Я не хочу, чтобы передать в функцию объединения, хотя, как я просто хочу throwingMerger(), точно так же, как основной toMap реализации следующим образом:

public static <T, K, U> 
     Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper, 
       Function<? super T, ? extends U> valueMapper) { 
    return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); 
} 

Что бы быть лучшим практика использования Collectors для возврата SortedMap?

ответ

35

Я не думаю, что вы можете получить намного лучше, чем это:

.collect(Collectors.toMap(keyMapper, valueMapper, 
         (v1,v2) ->{ throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2));}, 
         TreeMap::new)); 

где throw лямбда такой же, как throwingMerger(), но я не могу сразу назвать это, так как это частный пакет (вы можете, конечно, всегда сделать свой собственный статический метод, который, как throwingMerger() есть)

+0

I есть * точно * это. –

+0

Я собираюсь спуститься по обычным коллекционным маршрутам, так как я вижу похожие события, происходящие в будущем. –

+4

Параметр, который вы обозначили 'k', не является * ключом *, как подразумевается в письме, а скорее первым значением бинарной операции для слияния. – antak

8

на основе подтверждения dkatzel, что есть не хороший метод API, я выбрал для поддержания своего собственного пользовательских Коллекторы класса:.

public final class StackOverflowExampleCollectors { 

    private StackOverflowExampleCollectors() { 
     throw new UnsupportedOperationException(); 
    } 

    private static <T> BinaryOperator<T> throwingMerger() { 
     return (u, v) -> { 
      throw new IllegalStateException(String.format("Duplicate key %s", u)); 
     }; 
    } 

    public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, 
      Function<? super T, ? extends U> valueMapper, Supplier<M> mapSupplier) { 
     return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), mapSupplier); 
    } 

} 
+0

Вы должны изменить сообщение об исключении. Вы используете одно из значений (как это делал JDK: https://bugs.openjdk.java.net/browse/JDK-8040892), но сообщение подсказывает, что это ключ. Можно показать ключ (http://hg.openjdk.java.net/jdk9/dev/jdk/rev/8b80651ce43f), но это сложнее, поэтому, возможно, просто используйте 'throw new IllegalStateException (String.format (« Дублирующий ключ для значений% s и% s », u, v)); , – Martin

+0

Эй, @ Мартин, спасибо за ссылки! В этом примере я пытался опубликовать код дословно из частного метода 'throwingMerger' класса' Collectors', чтобы показать, что я работаю над тем, что он закрыт. Я полностью вижу, откуда вы пришли, и сообщение об ошибке, которое вы представляете, лучше, но, возможно, отвлекает от попытки сказать «сделать то же самое, модификатор видимости идет в пути»? Как насчет того, чтобы добавить ваше предложение в качестве редактирования внизу и привести источники? –

7

Кажется, что нет стандартного способа сделать это без определения вашего собственного метода throwingMerger() или использования явной лямбда. В моей библиотеке StreamEx я определил метод toSortedMap, который также uses мой собственный throwingMerger().

+4

Это похоже на надзор, не имея подписи метода, который берет поставщика карты. –

3

Другой способ, которым вы можете сделать это, - дать возможность Collectors.toMap() вернуть любую карту, которую он будет возвращать, а затем передать ее новому TreeMap <>().

Опасность заключается в том, что это работает только в том случае, если ваши «hashCode() + equals()» и «compareTo» являются согласованными. Если они несовместимы, то вы получите HashMap, удаляющий другой набор ключей, чем ваш TreeMap.