https://stackoverflow.com/questions/38087900/is-it-better-to-return-an-immutablemap-or-a-map
You should have
ImmutableMap
as your return type. Map
contains methods that are not supported by the implementation of ImmutableMap
(e.g. put
) and are marked @deprecated
in ImmutableMap
.
Using deprecated methods will result in a compiler warning & most IDEs will warn when people attempt to use the deprecated methods.
This advanced warning is preferable to having runtime exceptions as your first hint that something is wrong.
- If you are writing a public-facing API and that immutability is an important aspect of your design, I would definitely make it explicit either by having the name of the method clearly denotes that the returned map will be immutable or by returning the concrete type of the map. Mentioning it in the javadoc is not enough in my opinion.Since you're apparently using the Guava implementation, I looked at the doc and it's an abstract class so it does give you a bit of flexibility on the actual, concrete type.
- If you are writing an internal tool/library, it becomes much more acceptable to just return a plain
Map
. People will know about the internals of the code they are calling or at least will have easy access to it.
https://www.baeldung.com/guava-multimap
Multimaps are commonly used in places where a Map<K, Collection<V>> would otherwise have appeared. The differences include:
- There is no need to populate an empty collection before adding an entry with put()
- The get() method never returns null, only an empty collection (we do not need to check against null like in Map<String, Collection<V>> test case)
- A key is contained in the Multimap if and only if it maps to at least one value. Any operation that causes a key to has zero associated values, has the effect of removing that key from the Multimap (in Map<String, Collection<V>>, even if we remove all values from the collection, we still keep an empty Collection as a value, and this is unnecessary memory overhead)
- The total entry values count is available as size()
Guava’s Multimap interface has three immutable implementations – ImmutableMultimap, ImmutableListMultimap and ImmutableSetMultimap, which should always be preferred over the mutable implementations.
https://www.codota.com/code/java/methods/com.google.common.collect.Multimaps/toMultimap
public void testMultimapCollectorGenerics() { ListMultimap<Integer, String> unused = Stream.of("foo", "bar", "quux") .collect( Multimaps.toMultimap( String::length, s -> s, MultimapBuilder.treeKeys().arrayListValues()::build)); }https://stackoverflow.com/questions/30634608/java-8-collector-for-guava-immutable-collections
.collect(ImmutableSet.toImmutableSet())
.collect(Maps.toImmutableEnumMap())
.collect(Sets.toImmutableEnumSet())
.collect(Tables.toTable())
.collect(ImmutableList.toImmutableList())
.collect(Multimaps.toMultimap(...))
https://www.baeldung.com/guava-setsthe power set – the set of all possible subsets of that set
Set<Character> chars = ImmutableSet.of(
'a'
,
'b'
);
Set<Set<Character>> result = Sets.powerSet(chars);
Set<Character> empty = ImmutableSet.<Character> builder().build();
Set<Character> a = ImmutableSet.of(
'a'
);
Set<Character> b = ImmutableSet.of(
'b'
);
Set<Character> aB = ImmutableSet.of(
'a'
,
'b'
);
assertThat(result, contains(empty, a, b, aB));
ContiguousSet<Integer> set = ContiguousSet.create(
Range.closed(start, end), DiscreteDomain.integers());
assertEquals(21, set.size());
assertEquals(10, set.first().intValue());
assertEquals(30, set.last().intValue());
RangeSet. We can use RangeSet to hold disconnected and nonempty ranges.
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(
1
,
10
));
rangeSet.add(Range.closed(
12
,
15
));
assertEquals(
2
, rangeSet.asRanges().size());
rangeSet.add(Range.closed(
10
,
12
));
assertTrue(rangeSet.encloses(Range.closed(
1
,
15
)));
assertEquals(
1
, rangeSet.asRanges().size());
a Multiset does support adding duplicate elements – which it counts as occurrences.
we group a Listof names by their length using Multimaps.index():
public
void
whenGroupingListsUsingMultimap_thenGrouped() {
List<String> names = Lists.newArrayList(
"John"
,
"Adam"
,
"Tom"
);
Function<String,Integer> func =
new
Function<String,Integer>(){
public
Integer apply(String input) {
return
input.length();
}
};
Multimap<Integer, String> groups = Multimaps.index(names, func);
assertThat(groups.get(
3
), containsInAnyOrder(
"Tom"
));
assertThat(groups.get(
4
), containsInAnyOrder(
"John"
,
"Adam"
));
A RangeMap is a special kind of mapping from disjoint non-empty ranges to non-null values.
RangeMap<Integer, String> experienceRangeDesignationMap
= TreeRangeMap.create();
experienceRangeDesignationMap.put(
Range.closed(
0
,
2
),
"Associate"
);
experienceRangeDesignationMap.put(
Range.closed(
3
,
5
),
"Senior Associate"
);
experienceRangeDesignationMap.put(
Range.closed(
6
,
8
),
"Vice President"
);
experienceRangeDesignationMap.put(
Range.closed(
9
,
15
),
"Executive Director"
);
assertEquals(
"Vice President"
,
experienceRangeDesignationMap.get(
6
));
assertEquals(
"Executive Director"
,
experienceRangeDesignationMap.get(
15
));
}
There are two forms of equivalence, ‘identity’ equivalence and ‘equals’ equivalence.
In ‘identity’ equivalence, the objects being compared to are identical, that is, they share the same address in memory thus the objects refer to the same instance of a class.
In ‘equals’ equivalence, two objects are equivalent if the instances are of the same type and if the properties of the first object and second object are same.
The
Equivalence.equivalent(a, b)
follows- Reflexive – If x is a reference to an object then
equivalent(x, x)
should always returntrue
. - Symmetric – If x and y are references to objects of the same type then
equivalent(x, y)
andequivalent(y, x)
should return same result. - Transitive – If
equivalent(x, y)
returnstrue
andequivalent(y, z)
returnstrue
thenequivalent(x, z)
returnstrue
. - Consistent – If
equivalent(x, y)
is called multiple times it should return the same result as long as neither x not y is modified.
Guava’s
Equivalence
intends to fill this gap. In the same vein as a Comparator, using an Equivalence allows you to either define a different equals()/hashCode() strategy than the one already defined by your target class, or to define one for a target class which “has none at all” (meaning, in this case, that the class uses Object
‘s equals()
/hashCode()
implementations).