Как преобразовать карту <String, String> в карту <Long, String>? (опция: использование гуавы)

У меня есть Map<String, String> ключ String - это не что иное, как числовое значение, например "123" и т.д. Я получаю числовое значение, потому что эти значения поступают из пользовательского интерфейса в моем компоненте JSF. Я не хочу менять контракт с компонентом пользовательского интерфейса.

Теперь я хотел бы создать Map<Long, String> на основе вышеперечисленного Map, я видел несколько методов transform в классе Maps, но все они фокусируются на преобразовании, а не на ключе.

Есть ли лучший способ конвертировать Map<String, String> в Map<Long, String>?

Ответ 1

ОБНОВЛЕНИЕ для Java 8

Вы можете использовать потоки, чтобы сделать это:

Map<Long, String> newMap = oldMap.entrySet().stream()
  .collect(Collectors.toMap(e -> Long.parseLong(e.getKey()), Map.Entry::getValue));

Это предполагает, что все ключи являются допустимыми строковыми представлениями Long s. Также вы можете столкнуться при трансформации; например, "0" и "00" оба соответствуют 0L.


Я думаю, что вам придется перебирать карту:

Map<Long, String> newMap = new HashMap<Long, String>();
for(Map.Entry<String, String> entry : map.entrySet()) {
   newMap.put(Long.parseLong(entry.getKey()), entry.getValue());
}

Этот код предполагает, что вы санировали все значения в map (поэтому недопустимые длинные значения).

Я надеюсь, что есть лучшее решение.

РЕДАКТИРОВАТЬ

В Commons Collection-Utils я столкнулся с методом CollectionUtils#transformedCollection(Collection, Transformer) который выглядит так, как будто он может делать то, что вы хотите. Поцарапайте это, это работает только для классов, которые реализуют Collection.

Ответ 2

@Ответ Vivin правильный, но я считаю полезным объяснить, почему у Guava нет никакого способа разрешить вам преобразовать ключи Map (или вообще преобразовать Set).

Все методы Guava для преобразования и фильтрации создают ленивые результаты... функция/предикат применяется только тогда, когда это необходимо, когда объект используется. Они не создают копии. Из-за этого преобразование может легко нарушить требования Set.

Скажем, например, у вас есть Map<String, String>, который содержит как "1", так и "01" в качестве ключей. Они оба отличаются String s, и поэтому Map может легально содержать как клавиши. Если вы преобразуете их с помощью Long.valueOf(String), они оба сопоставляют значение 1. Они больше не являются отдельными ключами. Это ничего не сломает, если вы создадите копию карты и добавите записи, потому что любые дубликаты клавиш будут перезаписывать предыдущую запись для этого ключа. Ленообразно преобразованный Map, однако, не имел бы возможности использовать уникальные ключи и поэтому нарушил бы контракт Map.

Ответ 3

Теперь вы можете использовать поток Java 8, карту, собирать, чтобы сделать это более понятным и понятным образом.

Map<String, String> oldMap

Map<Long, String> newMap = oldMap.entrySet().stream()
  .collect(Collectors.toMap(entry -> Long.parseLong(entry.getKey()), Map.Entry::getValue));

Ответ 4

Здесь приведена обновленная версия одного из приведенных ниже ответов, чтобы сделать полученную карту неизменяемой (нет, она не использует Guava, просто Java 8):

  import static java.util.stream.Collectors.collectingAndThen;
  import static java.util.stream.Collectors.toMap;

  ...

  newMap = oldMap.entrySet().stream().collect(collectingAndThen(
              toMap((Map.Entry<String, String> entry) -> transformKey(entry.getKey()),
                    (Map.Entry<String, String> entry) -> transformValue(entry.getValue())),
              Collections::unmodifiableMap)));

Ответ 5

Короткий ответ - нет, Guava не предоставляет этот вариант из коробки.

Простым способом будет что-то вроде ниже. Однако есть некоторые предостережения.

    public static <K, V, L, W> Map<L, W> transformMap(Map<K, V> map, Function<K, L> keyFunction, Function<V, W> valueFunction) {
    Map<L, W> transformedMap = newHashMap();

    for (Entry<K, V> entry : map.entrySet()) {
        transformedMap.put(
                keyFunction.apply(entry.getKey()),
                valueFunction.apply(entry.getValue()));
    }

    return transformedMap;
}

public static <K, V, L> Map<L, V> transformKeys(Map<K, V> map, Function<K, L> keyFunction) {
    return transformMap(map, keyFunction, Functions.<V>identity());
}

Трансформаторы Guava все "ленивы" или основаны на просмотре. Я думаю, что для реализации трансформатора ключа карты вам понадобится двухсторонняя функция. Я понимаю, что есть конвертер, который находится в работе команды Guava, которая решила бы эту проблему.

Другая проблема, с которой вы столкнулись, заключается в том, что вам придется иметь дело с возможностью дублирования, чтобы быть "Jimmy-proof", еще одним принципом Guava. Один из способов справиться с этим - вернуть Multimap; другой - вызывать исключение, когда вы сталкиваетесь с дубликатами. То, что я бы не предложил, скрывает проблему, например. игнорируя последующие записи с дублирующимися ключами или перезаписывая новую запись дублирующимся ключом.

Ответ 6

Лучшее сообщение, чтобы предоставить готовые рабочие решения для этого, можно найти здесь:

Tranform HashMap в Java 8

Этот сайт показывает все 3 возможных случая:

  1. Карта → Карта
  2. Карта → Карта
  3. Карта → Карта