This issue arose because you are writing your code against concrete implementations. Which doesn't bays you anything. It only makes your code inflexible, more difficult to maintain, more verbose and less expressive. Consider reading this question What does it mean to "program to an interface"?.
In regard to Collectors.toMap()
(which expects only two arguments) currently, it does return you a HashMap
. And it is not specified, because sometime in the future it could be replaced with an enhanced general purpose implementation that will better in some cases than HashMap
. If you would write your code against Map
interface instead of requesting a particular implementation, you would get an implementation for free without changing the code.
As I've mentioned, you can request a particular implementation of the Map
interface by using a flavor of toMap()
that expects a supplier mapFactory
.
Collector<T,?,M> toMap(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapFactory)
But my suggestion is to use it when need a special purpose implementation like LinkedHashMap
, TreeMap
, WeakedHashMap
, etc. The best current general purpose implementation (which currently a HashMap
) will be provided to you by default, just write your code against interfaces, and you'll be fine.
An example of usage of this flavor of toMap
:
Map<String, String> result =
list.stream()
.collect(Collectors.toMap(
data->data[0],
data->data[1],
(left, right) -> { throw new AssertionError(); }, // this argument is meant to resolve duplicates and because all keys are expected to be unique it'll trows an error if duplicate will occure
LinkedHashMap::new));
In regard to new HashMap<String, String>(){{ someCode }};
, I suggest you to utilize Java 9 static methods of the Map
interface Map.of()
or Map.ofEntries()
instead. It makes your code leaner and saves from disadvantages that double brace initialization can bring, like possibility of memory leaks, which might emerge because the anonymous inner class that it creates under the hood would hold reference to the instance of the enclosing class. That's why this practice is considered to be an antipattern.