开发者

How to convert Map<String, String> to Map<Long, String> ? (option : using guava)

I have a Map<String, String&开发者_如何学运维gt; the String key is nothing but numeric value like "123" etc. I'm getting numeric value because this values are coming from the UI in my JSF component. I don't want to change the contract of UI component.

Now I would like to create a Map<Long, String> based on the above Map, I saw some transform methods in the Maps class but all are focusing on the converting value and not key.

Is there any better way to convert Map<String, String> to Map<Long, String> ?


@Vivin's answer is correct, but I think it's useful to explain why Guava doesn't have any method to allow you to transform the keys of a Map (or to transform a Set at all).

All of Guava's methods for transforming and filtering produce lazy results... the function/predicate is only applied when needed as the object is used. They don't create copies. Because of that, though, a transformation can easily break the requirements of a Set.

Let's say, for example, you have a Map<String, String> that contains both "1" and "01" as keys. They are both distinct Strings, and so the Map can legally contain both as keys. If you transform them using Long.valueOf(String), though, they both map to the value 1. They are no longer distinct keys. This isn't going to break anything if you create a copy of the map and add the entries, because any duplicate keys will overwrite the previous entry for that key. A lazily transformed Map, though, would have no way of enforcing unique keys and would therefore break the contract of a Map.


UPDATE for Java 8

You can use streams to do this:

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

This assumes that all keys are valid string-representations of Longs. Also, you can have collisions when transforming; for example, "0" and "00" both map to 0L.


I would think that you'd have to iterate over the map:

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

This code assumes that you've sanitized all the values in map (so no invalid long values).

I'm hoping there is a better solution.

EDIT

I came across the CollectionUtils#transformedCollection(Collection, Transformer) method in Commons Collection-Utils that looks like it might do what you want. Scratch that, it only works for classes that implement Collection.


You can now use Java 8 stream, map, collect to do this in a more readable, clean manner.

Map<String, String> oldMap

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


Here's an updated version of one of the answers below to make the resulting Map unmodifiable (no, it's not using Guava, just plain 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)));


The short answer is no, Guava does not provide this one out of the box.

The simple way would be something like below. There are some cautions, however.

    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's transformers are all "lazy" or view-based. I think to implement a map key transformer, you'd want a two-way function. My understanding is that there is a Converter that is in the works by the Guava team, which would solve that problem.

The other problem you run into is that you'd have to deal with the possibility of duplicates in order to be "Jimmy-proof", another Guava principle. One way to handle that would be to return a Multimap; another would be to throw an exception when you encounter duplicates. What I would not suggest is hiding the problem e.g. by ignoring subsequent entries with duplicate keys, or by overwriting the new entry with the duplicate key.


Answer using Java 8 and jOOλ

private static <K1, K2, V> Map<K2, V> mapKeys(Map<K1, V> map, Function<K1, K2> keyMapper) {
    return Seq.seq(map).map(t -> t.map1(keyMapper)).toMap(Tuple2::v1, Tuple2::v2);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜