Reputation: 3403
So I have the following HashMap:
HashMap<String, List<someDataType>> map
;
I want to create a new HashMap that is only composed of the k/v pairs in map
that have a value (the list) whose length is less than a certain "x". The only way I know how to do this is to iterate through the HashMap and put k/v pairs into a new HashMap. Is there a more concise way to achieve what I'm looking for? Thanks.
Upvotes: 5
Views: 8384
Reputation: 10972
Nowadays (Java 8+) this could be done with streams:
Predicate<Map.Entry<String, List<String>>> test = entry -> entry.getValue().size() <= x; // note this is java.util.function.Predicate
Map<String, List<String>> filteredMap = map.entrySet().stream().filter(test)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
This helps to avoid the dependency to guava which might be undesired.
Upvotes: 1
Reputation: 55223
If the Guava library is available to your project, you could use Maps.filterValues
(somewhat echoing Keith's answer):
final int x = 42;
Map<String, List<String>> filteredMap =
Maps.filterValues(map, new Predicate<Collection<?>>() {
@Override
public boolean apply(final Collection<?> collection) {
return collection.size() < x;
}
});
Map<String, List<String>> filteredMapCopy = ImmutableMap.copyOf(filteredMap);
Note the need for a copy because filterValues
returns a filtered view of the original map.
Update: with Java 8 you can simplify the predicate to a lambda expression:
Map<String, List<String>> filteredMap = Maps.filterValues(map, list -> list.size() < x);
Upvotes: 6
Reputation: 7899
Going along with the other Guava examples, you can use Guava's MultiMap
s:
final MultiMap<K, V> mmap = ArrayListMultiMap.create();
// do stuff.
final int limit = 10;
final MultiMap<K, V> mmapView =
MultiMaps.filterKeys(mmap, new Predicate<K>(){
public boolean apply(K k) {
return mmap.get(k).size() <= limit;
}
});
The MultiMaps.newListMultiMap
method takes arguments you don't want to provide. You can't use MultiMaps.filterValues
or .filterEntries
here because those use the individual values, not the lists of values. On the other hand, mmap.get(k)
never returns null
. You cam, of course, use a static inner class that you pass mmap
and limit
to instead of using anonymous inner classes.
Upvotes: 0
Reputation: 18303
You may want to look at the Guava library from Google. There's an enormous number of Collections
and Map
related utils in there, which let you do complex stuff quite concisely. An example of what you can do is:
Iterable<Long> list =
Iterables.limit(
Iterables.filter(
Ordering.natural()
.reverse()
.onResultOf(new Function<Long, Integer>() {
public Integer apply(Long id) {
return // result of this is for sorting purposes
}
})
.sortedCopy(
Multisets.intersection(set1, set2)),
new Predicate<Long>() {
public boolean apply(Long id) {
return // whether to filter this id
}
}), limit);
I'm sure you can find something in there which can do what you're looking for :-)
Upvotes: 0
Reputation: 4184
Using guava:
Map<String, List<String>> newMap =
Maps.filterEntries(originalMap, new MyEntryPredicate(10));
where:
private static class MyEntryPredicate implements Predicate<Map.Entry<String, List<String>>> {
// max list length, exclusive
private int maxLength;
private MyEntryPredicate(int maxLength) {
this.maxLength = maxLength;
}
@Override
public boolean apply(Map.Entry<String, List<String>> input) {
return input != null && input.getValue().size() < maxLength;
}
}
Upvotes: 11
Reputation: 136062
Alternatevely you can make a copy of the original map and iterate over the values removing those whose length is less than x.
Upvotes: -1