Intersection of java.util.Map - java

Is there a method in java.util.Map or any util to perform an intersection on two maps? (To intersect two maps by the "keys")
I am not able to find any. I can always implement my own intersection logic, but I was hoping there is already some operation in one of the java.util.* classes that would do this.

How about:
Map map1 = ...;
Map map2 = ...;
Map result = new ...(map1);
result.keySet().retainAll(map2.keySet());
or:
Map map1 = ...;
Map map2 = ...;
Set result = new ...(map1.keySet());
result.retainAll(map2.keySet());

If you're using Guava, you can use Maps.difference to get a MapDifference object, from which you can extract the entriesInCommon() and entriesDiffering() as maps. (Disclosure: I contribute to Guava.)

Guava's Sets.intersection(Set, Set) should do the job, with the keySet of each Map passed in as parameters.

I would recommend apache collectionUtils#intersection
Do the following:
Collection intersection=
CollectionUtils.intersection(map1.keySet(),map2.keySet());

To test for intersection you can use the containsAll() operation. Which 'Returns true if this set contains all of the elements of the specified collection. If the specified collection is also a set, this method returns true if it is a subset of this set.'
To get a collection of these intersecting elements you can use the retainAll() operation instead.
These methods are both found here
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Set.html

Loop over one map's keys, see if they're in the second map:
private Map getIntersection(Map mapOne, Map mapTwo)
{
Map intersection = new HashMap();
for (Object key: mapOne.keySet())
{
if (mapTwo.containsKey(key))
intersection.put(key, mapOne.get(key));
}
return intersection;
}

Related

How to get the first key value from map using JAVA 8?

As for now I am doing :
Map<Item, Boolean> processedItem = processedItemMap.get(i);
Map.Entry<Item, Boolean> entrySet = getNextPosition(processedItem);
Item key = entrySet.getKey();
Boolean value = entrySet.getValue();
public static Map.Entry<Item, Boolean> getNextPosition(Map<Item, Boolean> processedItem) {
return processedItem.entrySet().iterator().next();
}
Is there any cleaner way to do this with java8 ?
I see two problems with your method:
it will throw an exception if the map is empty
a HashMap, for example, has no order - so your method is really more of a getAny() than a getNext().
With a stream you could use either:
//if order is important, e.g. with a TreeMap/LinkedHashMap
map.entrySet().stream().findFirst();
//if order is not important or with unordered maps (HashMap...)
map.entrySet().stream().findAny();
which returns an Optional.
Seems like you need findFirst here
Optional<Map.Entry<Item, Boolean>> firstEntry =
processedItem.entrySet().stream().findFirst();
Obviously a HashMap has no order, so findFirst might return a different result on different calls. Probably a more suitable method would be findAny for your case.

Is it possible to instantiate a Map with a list of keys?

Usually, if I know beforehand all the keys of a map, I instantiate it like this:
List<String> someKeyList = getSomeList();
Map<String, Object> someMap = new HashMap<String, Object>(someKeyList.size());
for (String key : someKeyList) {
someMap.put(key, null);
}
Is there any way to do this directly without needing to iterate through the list? Something to the effect of:
new HashMap<String, Object>(someKeyList)
My first thought was to edit the map's keyset directly, but the operation is not supported. Is there other way I'm overlooking?
You can use Java 8 Streams :
Map<String,Object> someMap =
someKeyList.stream()
.collect(Collectors.toMap(k->k,k->null));
Note that if you want a specific Map implementation, you'll have to use a different toMap method, in which you can specify it.

How can I compare two MultiMaps?

I have two Multimaps which have been created from two huge CSV files.
Multimap<String, SomeClassObject> mapOne = ArrayListMultimap.create();
Multimap<String, SomeClassObject> mapTwo = ArrayListMultimap.create();
I have assumed one CSV column to be as a Key and each of the Key has thousands of values associated with it. Data contained within these Multimaps should be same. Now I want to compare the data within these Multimaps and find if any values are different. Here are the two approaches I am thinking of:
Approach One:
Make one big list from the Multimap. This big list will contain a few individual lists. Each of the smaller lists contains a unique value which is the "key" read from Multimap along with its associated values, which will form the rest of that individual list.
ArrayList<Collection<SomeClassObject>> bigList = new ArrayList<Collection<SomeClassObject>>();
Within bigList will be individual small lists A, B, C etc.
I plan on picking individual lists from each bigList of the two files on the basis of checking that individual list from second Multimap contains that "key" element. If it does, then compare both of these lists and find anything that could not be matched.
Approach Two:
Compare both the Multimaps but I am not sure how will that be done.
Which approach should have smaller execution time? I need the operation to be completed in minimum amount of time.
Use Multimaps.filterEntries(Multimap, Predicate).
If you want to get the differences between two Multimaps, it's very easy to write a filter based on containsEntry, and then use the filtering behavior to efficiently find all the elements that don't match. Just build the Predicate based on one map, and then filter the other.
Here's what I mean. Here, I'm using Java 8 lambdas, but you can look at the revision history of this post to see the Java 7 version:
public static void main(String[] args) {
Multimap<String, String> first = ArrayListMultimap.create();
Multimap<String, String> second = ArrayListMultimap.create();
first.put("foo", "foo");
first.put("foo", "bar");
first.put("foo", "baz");
first.put("bar", "foo");
first.put("baz", "bar");
second.put("foo", "foo");
second.put("foo", "bar");
second.put("baz", "baz");
second.put("bar", "foo");
second.put("baz", "bar");
Multimap<String, String> firstSecondDifference =
Multimaps.filterEntries(first, e -> !second.containsEntry(e.getKey(), e.getValue()));
Multimap<String, String> secondFirstDifference =
Multimaps.filterEntries(second, e -> !first.containsEntry(e.getKey(), e.getValue()));
System.out.println(firstSecondDifference);
System.out.println(secondFirstDifference);
}
Output is the element that is not in the other list, in this contrived example:
{foo=[baz]}
{baz=[baz]}
These multimaps will be empty if the maps match.
In Java 7, you can create the predicate manually, using something like this:
public static class FilterPredicate<K, V> implements Predicate<Map.Entry<K, V>> {
private final Multimap<K, V> filterAgainst;
public FilterPredicate(Multimap<K, V> filterAgainst) {
this.filterAgainst = filterAgainst;
}
#Override
public boolean apply(Entry<K, V> arg0) {
return !filterAgainst.containsEntry(arg0.getKey(), arg0.getValue());
}
}
Use it as an argument to Multimaps.filterEntries() like this:
Multimap<String, String> firstSecondDifference =
Multimaps.filterEntries(first, new FilterPredicate(second));
Multimap<String, String> secondFirstDifference =
Multimaps.filterEntries(second, new FilterPredicate(first));
Otherwise, the code is the same (with the same result) as the Java 8 version above.
From the ArrayListMultimap.equals doc:
Compares the specified object to this multimap for equality.
Two ListMultimap instances are equal if, for each key, they contain the same values in the same order. If the value orderings disagree, the multimaps will not be considered equal.
So just do mapOne.equals(mapTwo). You won't have a better execution time by trying to do it yourself.

Retrieving the previous key-map value before it was overwritten in a HashMap

I have created a HashMap as per my code...
HashMap map=new HashMap();//HashMap key random order.
map.put("Amit","Java");
map.put("Saral","J2EE");
map.put("Saral","Andriod");//same key but different value
map.put("Nitin","PHP");
map.put("hj","Spring1");
System.out.println("There are "+map.size()+" elements in the map.");
System.out.println("Content of Map are...");
Set s=map.entrySet();
Iterator itr=s.iterator();
while(itr.hasNext()){
Map.Entry m=(Map.Entry)itr.next();
System.out.println(m.getKey()+"\t"+m.getValue()+"\t"+ m.hashCode());
}
When I execute this code, the value for key=Saral is Android. Is there any way that I can get the previous value for this key, which was J2EE?
No, you can't have that with a standard HashMap. The easiest solution would be to store a List as value in the map though, and then you can add multiple items to the list (Btw you should use generic collections too). To simplify, you could use a helper method like this:
void addToMap(Map<String, List<String>> map, String key, String value) {
List<String> list = map.get(key);
if (list == null) {
list = new ArrayList<String>();
map.put(key, list);
}
list.add(value);
}
Map<String, List<String>> map = new HashMap<String, List<String>>();
addToMap(map, "Amit", "Java");
addToMap(map, "Saral", "J2EE");
addToMap(map, "Saral", "Andriod");//same key but different value
addToMap(map, "Nitin", "PHP");
addToMap(map, "hj", "Spring1");
...
The helper method here is just an illustration - a full, robust implementation may need to include e.g. checks for duplicate values, depending on whether you allow them. If not, you may prefer using a Set instead of List.
Update
To print out the contents of this map, you need to use an embedded loop to iterate through the list of values for each map entry (btw you can use a foreach loop instead of an iterator):
for (Map.Entry<String, List<String>> m : map.entrySet())
{
for (String v : m.getValue())
{
System.out.println(m.getKey()+"\t"+v+"\t"+ m.hashCode());
}
}
A Map can contain at most one entry per key, so when you call map.put("Saral","Andriod"), the old "J2EE" value is removed. To support multiple values per key, you would need to maintain a Map<String, List<String>> or else a multi-map implementation such as Guava's Multimap.
As a side note I would recommend you start using generics, for example Map<String, String>, Iterator<String>, etc. for type safety at compile time.
The old value is overwritten (replaced). There will be only one mapping (entry) for one unique key. There fore it does not exist anymore so you can not retrieve it.
You cannot do this with standard implementations of Map that Java provides. However there are implementations of MultiMap (that's basically what you're after).
One example is this one from Google:
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/Multimap.html
Note that you won't be able to just get this one interface, you'll need a few classes along with it.
As other have said, this won't work with a standard Map. However, Google's Guava provides a MultiMap interface, which you can use to store multiple values with a single key.
Example of use:
Multimap<String,String> multiMap = ArrayListMultimap.create();
multiMap.put("color", "red");
multiMap.put("color", "blue");
System.out.println(multiMap.get("color")); //returns a ["red', "blue"] list

Using the keySet() method in HashMap

I have a method that goes through the possible states in a board and stores them in a HashMap
void up(String str){
int a = str.indexOf("0");
if(a>2){
String s = str.substring(0,a-3)+"0"+str.substring(a-2,a)+str.charAt(a-3)+str.substring(a+1);
add(s,map.get(str)+1);
if(s.equals("123456780")) {
System.out.println("The solution is on the level "+map.get(s)+" of the tree");
//If I get here, I need to know the keys on the map
// How can I store them and Iterate through them using
// map.keySet()?
}
}
}
I'm interested in the group of keys. What should I do to print them all?
HashSet t = map.keySet() is being rejected by the compiler as well as
LinkedHashSet t = map.keySet()
Use:
Set<MyGenericType> keySet = map.keySet();
Always try to specify the Interface type for collections returned by these methods. This way regardless of the actual implementation class of the Set returned by these methods (in your case map.keySet()) you would be ok. This way if the next release the jdk guys use a different implementation for the returned Set your code will still work.
map.keySet() returns a View on the Keys of the map. Making changes to this view results in changing the underlying map though those changes are limited. See the javadoc for Map:
http://java.sun.com/j2se/1.5.0/docs/api/java/util/Map.html#keySet%28%29
Map<String, String> someStrings = new HashMap<String, String>();
for(Map.Entry<String, String> entry : someStrings.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
This is how I like to iterate through Maps. If you specifically want just the keySet(), that answer is elsewhere on this page.
for ( String key : map.keySet() ) {
System.out.println( key );
}
Set t = map.ketSet()
The API does not specify what type of Set is returned.
You should try to declare variables as the interface rather than a particular implementation.
Just
Set t = map.keySet();
Unless you're using an older JDK, I think its a little cleaner to use generics when using the Collections classes.
So thats
Set<MyType> s = map.keySet();
And then if you just iterate through them, then you can use any kind of loop you'd like. But if you're going to be modifying the map based on this keySet, you you have to use the keySet's iterator.
All that's guaranteed from keySet() is something that implements the interface Set. And that could possibly be some undocumented class like SecretHashSetKeys$foo, so just program to the interface Set.
I ran into this trying to get a view on a TreeSet, the return type ended up being TreeSet$3 on close examination.
Map<String, Object> map = new HashMap<>();
map.put("name","jaemin");
map.put("gender", "male");
map.put("age", 30);
Set<String> set = map.keySet();
System.out.println("this is map : " + map);
System.out.println("this is set : " + set);
It puts the key values in the map into the set.
From Javadocs HashMap has several methods that can be used to manipulate and extract data from a hasmap.
public Set<K> keySet()
Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.
Specified by:
keySet in interface Map
Overrides:
keySet in class AbstractMap
Returns:
a set view of the keys contained in this map
so if you have a map myMap of any datatype , such that the map defined as map<T> , if you iterate it as follows:
for (T key : myMap.keySet() ) {
System.out.println(key); // which represent the value of datatype T
}
e.g if the map was defined as Map<Integer,Boolean>
Then for the above example we will have:
for (Integer key : myMap.keySet()){
System.out.println(key) // the key printed out will be of type Integer
}

Categories

Resources