4

I have 2 HashMaps with millions of records. For simplicity, I will deal with only few records. I want to find the values which are in map a that are not in map b. Is there a function to do this? What's the quickest way to go about it?

Map a = new HashMap();
a.put(1, "big");
a.put(2, "hello");
a.put(3, "world");

Map b = new HashMap();

b.put(1,"hello");
b.put(2, "world");

In this case, output should be "big" since it is in a and not in b.

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Jany Gwan
  • 77
  • 1
  • 2
  • 9
  • 1
    you'll have to go for a linear approach. (foreach over map `a` look if map `b` contains the value. save the values which are not in b (or process them directly)) – ParkerHalo Jan 28 '16 at 14:23

6 Answers6

10

You are looking for the removeAll operation on the values of the map.

public static void main(String[] args) {
    Map<Integer, String> a = new HashMap<>();
    a.put(1, "big");
    a.put(2, "hello");
    a.put(3, "world");

    Map<Integer, String> b = new HashMap<>();
    b.put(1,"hello");
    b.put(2, "world");

    a.values().removeAll(b.values()); // removes all the entries of a that are in b

    System.out.println(a); // prints "{1=big}"
}

values() returns a view of the values contained in this map:

Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.

So removing elements from the values effectively removes the entries. This is also documented:

The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll and clear operations.


This removes from the map in-place. If you want to have a new map with the result, you should call that method on a new map instance.

Map<Integer, String> newMap = new HashMap<>(a);
newMap.values().removeAll(b.values());

Side-note: don't use raw types!

Community
  • 1
  • 1
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • Exactly what i need! Thanks! – Jany Gwan Jan 28 '16 at 14:30
  • There's also little bit different approach, if we could write our implementation of map. We could cast that 'a' map into OurMap overload addAll() so it won't overwrite values (skip instead) and value that are not present could collect into some variable etc. Ensuring that we won't mess up those Maps. ConcurrentHashMap got nice putIfAbsent method which is atomic ;) – Fincio Jan 28 '16 at 14:38
  • You don't nned to make a copy of the map, only of the values. Better: `newset= new HashSet<>(a.values()); newset.removeAll(b.values())` – leonbloy Jan 28 '16 at 14:38
2

The solution of @Tunaki will work fine, is readable and short.

Just for the sake of completeness the solution "by hand":

for (String s : a.values()) {
    if (!b.containsValue(s)) {
        System.out.println (s);
        // process the value (e.g. add it to a list for further processing)
    }
}
ParkerHalo
  • 4,341
  • 9
  • 29
  • 51
2

If you're allowed to use Apache Commons Collections 4, you can use SetUtils.difference(), which probably has similar performance to @Tunaki's answer.

david
  • 997
  • 6
  • 15
1

Here is a fast, non-destructive, solution based on streams:

Map<Integer, String> a = ...;
Map<Integer, String> b = ...;
Set<String> bVal = new HashSet<String>(b.values());
String[] res = a.values()
    .stream()
    .filter(s -> bVal.contains(s))
    .toArray(String[]::new);

res contains all values present in both maps. Upon completion of this code both maps remain in their original state.

The code requires additional memory of the size proportional to the size of the second map. If one of your maps is significantly smaller than the other, you could save space by using the smaller map as map b in the example above.

Demo.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0
A.containsValue(value) && !B.containsValue(value)

See: https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#containsValue(java.lang.Object)

osanger
  • 2,276
  • 3
  • 28
  • 35
0

I am assuming that the unique values u want does not depend on the key position (2, "hello" and 1,"hello")

A single line of code should do it.

a.values().removeAll(b.values());

caution: this removes all the repeating values from 'a' hashmap

Saketh Kotha
  • 101
  • 1
  • 1
  • 10