Hi Frank, On Apr 22, 2014, at 12:10 PM, Frank van Heeswijk <fvheesw...@outlook.com> wrote: > > Hello, > > First of all, this is the first time I am sending a mail to any java mailing > list,
Welcome! > so please correct me if I did something wrong. > > I am a regular visitor and answered on stackoverflow ( > http://stackoverflow.com/users/2057294/skiwi ) and noticed that there are > quite a large amount of questions now already asking for a way to easily > replace values in maps when their type fundamentally changes. > I however also have a few concerns while doing this, which will be listed > below. > > Take first as example the following code, it may not be the best example, but > it should show the intention: > > Map<String, Integer> map = new HashMap<>(); > map.put("test", 5); > Map<String, String> mapped = map.entrySet().stream() > .collect(Collectors.toMap(Map.Entry::getKey, entry -> > String.valueOf(entry.getValue()))); > > The intention here was to map the integer values to their string counterparts > in a single map, functionally this is equivalent to the following: > > Map<String, Integer> map = new HashMap<>(); > map.put("test", 5); > map.replaceAll((k, v) -> v + 1); > > One would expect the first case to also be a one-liner, possibly somewhat > like: > > Map<String, String> mapped = map.replaceAllSpecial((k, v) -> > String.valueOf(v)); > > Based on this I have the following concerns: > 1. How would the method exactly need to look like? Should it also have the > option to take a Function<V_IN, V_OUT> valueMapper as argument? > 2. How could this be optimized? Surely we do not *really* want to take all > map entries out of one map and insert the modified value in a new map, I was > thinking that an in-place modification would be the best, but then you have > the issue where the type changes. > Such in place modification is a definitely wrong, you are going to pollute your map so any previous refs to Map<String, Integer> are going to break with a ClassCastException when values are accessed. To be safe you would need to consistently use Map<String, Object> in such cases. (We don't have union types so you could declare <String | Integer> if you knew up front the types.) Unfortunately stream-based manipulating maps is more difficult than manipulating lists, as you have noted. One possible solution is to create some more map-oriented collectors, such as: public static <T, K, U> Collector<Map.Entry<K, T>, ?, Map<K,U>> toMapValue(Function<? super T, ? extends U> valueMapper) { return Collectors.toMap(Map.Entry::getKey, e -> valueMapper.apply(e.getValue())); } Map<String, Integer> a = new HashMap<>(); ... Map<String, String> b = a.entrySet().stream().collect(toMapValue(v -> v.toString())); That at least reduces some of the boilerplate and is easier for developers to initially grok. Generally stream-based operations on maps could be improved with a BiStream<K, V> so you could do: map.stream().mapValue(v -> v.toString()).collect(toMap()); We prototyped that (i have such an internal prototype hacked into the latest API) but decided we were not ready to commit to such an API in Java 8 (a number of cases where BiStream may have been useful were subsumed by Collectors.groupingBy), but it still might have value in a future release. We could add some common basic functionality to the default methods on Map e.g.: default Map<K, U> transformValues(Function<? super V, ? extends U> valueMapper) { ... } default Map<V, K> swap() throws IllegalStateException; // throws for dup keys etc. but there is always a danger of adding too much, a high bar would have to set set for that IMHO, a BiStream view of a Map is more satisfying in this respect and aligns with a Stream view of a Collection. Hth, Paul. > Lastly, I hope that we can agree on that for such a trivial usecase, the > usage of streams is possibly too complicated and furthermore point (2) is > very important. > > As some more reference, here are some of the stackoverflow questions that ask > for it: > - > http://stackoverflow.com/questions/23213891/how-to-map-values-in-a-map-in-java-8 > - > http://stackoverflow.com/questions/22840170/in-java-8-functional-style-how-can-i-map-the-values-over-a-java-util-map > > Regards, > > Frank van Heeswijk > >