I was working on a program to validate that a map contains some set of values or not. Map API is having one method called as map.containsValue(string). But, this method verify complete String as value.
import java.util.HashMap;
import java.util.Map;
public class TestMap {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(6, "P_T");
map.put(7, "P_Q_T");
map.put(8, "T");
map.put(9, "A");
map.put(10, "B");
map.put(11, "P_A");
map.put(1, "P_1");
map.put(2, "Q");
map.put(3, "P_Q");
map.put(4, "Q_T");
map.put(5, "T");
System.out.println("Map is = "+map);
System.out.println("Result is = " + map.containsValue("P"));
}
}
Above program will return output as :
Map is = {1=P_1, 2=Q, 3=P_Q, 4=Q_T, 5=T, 6=P_T, 7=P_Q_T, 8=T, 9=A, 10=B, 11=P_A}
Result is = false
My requirement is that Result should be true as, Map contains keys 1, 3, 6, 7, 11, which contains char P as value.
I have a solution, that i could use loop to verify each value and then find indexOf each value for P char.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class TestMap {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(6, "P_T");
map.put(7, "P_Q_T");
map.put(8, "T");
map.put(9, "A");
map.put(10, "B");
map.put(11, "P_A");
map.put(1, "P_1");
map.put(2, "Q");
map.put(3, "P_Q");
map.put(4, "Q_T");
map.put(5, "T");
System.out.println("Map is = " + map);
System.out.println("Result is = " + map.containsValue("P"));
System.out.println("Result from loop is = " + verifyMap(map));
}
private static boolean verifyMap(Map<Integer, String> map) {
// TODO Auto-generated method stub
Set<Integer> set = map.keySet();
Iterator<Integer> itr = set.iterator();
while (itr.hasNext()) {
Integer integer = (Integer) itr.next();
String str = map.get(integer);
if (str.indexOf("P") >= 0)
return true;
}
return false;
}
}
I have evaluated string with indexOf method instead of contains, which is marginally faster .
See this question
This return the desired result as:
Result from loop is = true
But, I just want to know, is there any other way to verify same?
You can write your method with less code using Java 8 Streams :
private static boolean verifyMap(Map<Integer, String> map) {
return map.values().stream().anyMatch(str->str.indexOf("P") >= 0);
}
Even in Java 7 your method can be shorter :
private static boolean verifyMap(Map<Integer, String> map) {
for (String str : map.values()) {
if (str.indexOf("P") >= 0)
return true;
}
return false;
}
You can also try this:
private static boolean verifyMap(Map<Integer, String> map) {
for(String val:map.values())
{
if( val.contains("P"))
{
return true;
}
}
return false;
}
I was able to find out a way by which I could verify partial char in the values of map.
I have used Matcher API from Java-7. Thanks to #Eran, for a hint related to Java8.
Below is the code snippet, i am using now.
private static boolean verifyMap(Map<Integer, String> map) {
Matcher matcher = Pattern.compile(".*p.*", Pattern.CASE_INSENSITIVE)
.matcher(map.values().toString());
return matcher.matches();
}
Why are you searching the values of a hashtable in the first place? That's guaranteed to be at least O(N) (and stringifying the whole thing, as you suggested elsewhere, is even worse since it also requires O(N) extra allocations and aa large constant factor as well). What you want is to build an inverted index which maps backward (from strings to integers). Most likely you want to use a trie, which will give you O(M) (where M is the maximum string length) lookup performance.
Related
I have written a code snippet to read a sentence and print the words in the sentence along with their count of occurence.
Example:
String = Java is a language. java is easy and i like Java
Expected output :
Java =3, is=2 a=1, language=1, easy=1, and=1 i=1, like=1
I want to achieve it by using two nested for loops but I am missing something and the code is broken. Here is the snippet
package corejava;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class DuplicateStringOccurence {
public static void main(String[] args) {
// TODO Auto-generated method stub
String myString = " Java is a language. java is easy and i like Java";
String[] wordsInMySentence = getWords(myString);
Map<String, Integer> myMap = new HashMap<String, Integer>();
int countOfOccurence = 1;
// outloop i, innerlop j
for(int i=0;i<wordsInMySentence.length;i++) {
if (myMap.containsKey(wordsInMySentence[i])) {
countOfOccurence=1;
continue;
}
for(int j=i+1;j<wordsInMySentence.length;j++)
{
if (wordsInMySentence[i].equalsIgnoreCase(wordsInMySentence[j])) {
// match found
countOfOccurence++;
}
myMap.put(wordsInMySentence[i], countOfOccurence);
}
}
// print the duplicates and counts
for (Entry<String, Integer> entry : myMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue().toString());
}
}
private static String[] getWords(String myString) {
// TODO Auto-generated method stub
String[] wordsInMySentence = myString.replaceAll("[^a-zA-Z ]", "").toLowerCase().split("\\s+");
// create the array of words from the sentence
for (String s:wordsInMySentence) {
// System.out.println(s);
}
return wordsInMySentence;
}
}
I am not getting the expected output. I want to correct this piece of code . Can someone guide what is the mistake here?
Using java8 you can do like below :
First split you string by using regex ("[. ]+") and store that into List.
Then using Collectors.toMap ,
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper,BinaryOperator<U> mergeFunction)
It takes below three arguments :
KeyMapper - k -> k.toLowerCase()
ValueMapper - k -> 1
MergeFunction - Here Integer::sum
You can refer doc Collectors.toMap(keyMapper,valueMapper,mergeFunction)
public class WordCount {
public static void main(String[] args) {
String sentense= "Java is a language.java is easy and i like Java";
List<String> list = Stream.of(sentense).map(k -> k.split("[. ]+")).flatMap(Arrays::stream)
.collect(Collectors.toList());
Map<String, Integer> countMap= list.stream()
.collect(Collectors.toMap(k -> k.toLowerCase(), k -> 1, Integer::sum));
System.out.println(countMap);
// Output : {a=1, java=3, like=1, and=1, i=1, language=1, is=2, easy=1}
}
}
With Map data structure you need only one for loop. Here is one solution using basic for loop:
String sentense= "Java, is a language.java is easy and i like Java";
String[] words = sentense.split("\\W+");
Map<String, Integer> countMap = new HashMap<>();
for(String word : words) {
word = word.toLowerCase();
Integer count = countMap.containsKey(word)? countMap.get(word) + 1 : 1;
countMap.put(word, count);
}
System.out.println(countMap);
You can use the handy merge method to keep track of the sums
private static String[] getWords(String myString) {
return myString.trim().split("\\W+");
}
public static void main(String[] args) {
String myString = "\tJava is a language. java is easy and i like Java ";
Map<String, Integer> myMap = new HashMap();
for (String word : getWords(myString)) {
myMap.merge(word.toLowerCase(), 1, Integer::sum);
}
System.out.println(myMap);
}
I have written the following code for a Pair class in Java using Map.Entry. Does anyone have any recommendations for improving it? The most important criteria for me that the class needed to meet were that it would be easy to construct a Pair and that .equals would work the way I wanted it to (equality of the two objects in the Pair in the same order means equality of the Pairs). The code seems to function as intended and is not used in anything that is taxing my system's memory yet, but any optimizations would be appreciated.
import java.util.*;
public class Pair {
// Return a map entry (key-value pair) from the specified values
public static <T, U> Map.Entry<T, U> of(T first, U second) {
return new AbstractMap.SimpleEntry<>(first, second);
}
private Map.Entry entry;
public Pair (Object x, Object y) {
Set<Map.Entry> entries = new HashSet<>();
entries.add(Pair.of(x, y));
Object[] setArray = entries.toArray();
this.entry = (Map.Entry) setArray[0];
}
public Object getKey() {
return entry.getKey();
}
public Object getValue() {
return entry.getValue();
}
#Override
public boolean equals(Object o)
{
if (o instanceof Pair) {
Pair pair = (Pair) o;
return ( (entry.getKey().equals(pair.getKey())) && (entry.getValue().equals(pair.getValue())) );
}
return false;
}
#Override
public int hashCode() {
return Objects.hash(entry.getKey(), entry.getValue());
}
#Override
public String toString() {
return String.format( "(" +entry.getKey().toString() + ", " + entry.getValue().toString() + ")" );
}
}
I have been considering using Collections.singletonMap instead of Map.Entry.
So far, I have successfully tested my code using the following:
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<Pair, Integer> testMap = new HashMap<>();
Pair onThrup = new Pair(1, 3);
System.out.println(onThrup); //Prints contents of Pair, not hash
testMap.put(onThrup, 7);
Pair unoTres = new Pair(1, 3);
System.out.println(onThrup.equals(unoTres));
System.out.println(testMap.containsKey(onThrup));
System.out.println(unoTres.hashCode() == onThrup.hashCode());
System.out.println(testMap.containsKey(unoTres)); // Should be, and is, true
}
}
Java 16 includes Record Classes:
public record Pair<K, V>(K key, V value) {
// intentionally empty
}
Creates a class with final fields, methods to retrieve key and value, equals, hashCode and toString methods.
Usage example:
var one = new Pair<Integer, String>(1, "one");
var two = new Pair(2, "two");
System.out.println(one); // Pair[key=1, value=one]
if (!one.equals(two))
System.out.printf("not same as Pair for %d%n", two.key());
not sure what is best, if available (java 16) I would use that
JLS 8.10
JEP 395
I have a scenario where I need to take the keys of a Map<String, Set<String>>, and add them into a new Set<String> that is sorted. The sort order is based on the Map values for each key. The value for each key of the map is a Set containing other keys that are related to that key.
I need the keys to be sorted in such a way that a related key must be BEFORE another key that contains it in its related Set. To use a programming paradigm, it's similar to requiring a variable to be declared on an earlier line, before it can be referenced on another line.
For example, the following represents the contents of the Map<String, Set<String>>:
abc=[def, ghi, jkl, mno]
def=[]
ghi=[def]
jkl=[ghi, stu]
mno=[]
pqr=[abc]
stu=[def]
vwx=[mno, ghi]
zy0=[jkl]
In this example, the key "jkl" has a relationship to keys, "ghi" and "stu", "def" does not have a relationship to any of the keys.
NOTE: The relationships will be ONE-WAY only. So, for example, if "ghi" is related to "def", "def" will NEVER be related to "ghi".
So, for the above Map, the sort order would be:
def=[]
mno=[]
ghi=[def]
stu=[def]
vwx=[mno, ghi]
jkl=[ghi, stu]
zy0=[jkl]
abc=[def, ghi, jkl, mno]
pqr=[abc]
Here's the Comparator that I wrote. It's inside of a runnable test class that uses the example above:
import java.util.*;
public class RelationshipComparator_Test {
public static void main(String[] args) {
String[] testMap = "abc=[def,ghi,jkl,mno]|def=[]|ghi=[def]|jkl=[ghi,stu]|mno=[]|pqr=[abc]|stu=[def]|vwx=[mno,ghi]|zy0=[jkl]".split("[|]");
Map<String, Set<String>> relationshipMap = new HashMap<>();
for (String entry : testMap) {
String[] keyValue = entry.split("[=]");
String replacement = keyValue[1].replaceAll("[^a-z0-9,]", "");
Set<String> valueSet = new HashSet<>();
String[] values = (!replacement.equals("") ? replacement.split("[,]") : new String[0]);
Collections.addAll(valueSet, values);
relationshipMap.put(keyValue[0], valueSet);
}
Set<String> sortedKeys = new TreeSet<>(new RelationshipComparator(relationshipMap));
sortedKeys.addAll(relationshipMap.keySet());
for (String key : sortedKeys) {
System.out.println(key + "=" + relationshipMap.get(key));
}
}
static class RelationshipComparator implements Comparator<String> {
private Map<String, Set<String>> relationshipMap;
RelationshipComparator(Map<String, Set<String>> relationshipMap) {
this.relationshipMap = relationshipMap;
}
#Override
public int compare(String o1, String o2) {
Set<String> o1Set = relationshipMap.get(o1);
Set<String> o2Set = relationshipMap.get(o2);
if (o1Set != null && o2Set != null) {
if (o1Set.size() == 0 && o2Set.size() > 0) {
printCompare(o1, o2, "o1Set.size() == 0: -1");
return -1;
}
if (o2Set.size() == 0 && o1Set.size() > 0) {
printCompare(o1, o2, "o2Set.size() == 0: 1");
return 1;
}
if (o1Set.contains(o2)) {
printCompare(o1, o2, "o1Set.contains(o2): 1");
return 1;
}
if (o2Set.contains(o1)) {
printCompare(o1, o2, "o2Set.contains(o1): -1");
return -1;
}
}
printCompare(o1, o2, "default: " + o1.compareTo(o2));
return o1.compareTo(o2);
}
private void printCompare(String o1, String o2, String result) {
System.out.println("**********");
System.out.println("o1: " + o1 + "=" + relationshipMap.get(o1));
System.out.println("o2: " + o2 + "=" + relationshipMap.get(o2));
System.out.println("result: " + result);
System.out.println("**********");
System.out.println();
}
}
}
If you run the code, you'll see the following output:
def=[]
mno=[]
ghi=[def]
jkl=[stu, ghi]
abc=[def, ghi, jkl, mno]
pqr=[abc]
stu=[def]
vwx=[ghi, mno]
zy0=[jkl]
It's incorrect because, "jkl" references "stu", but "stu" is sorted after "jkl".
Any help would be greatly appreciated.
You say that relationships are one-way, which rules out obvious cases such as:
a=[b]
b=[a]
for which no solution is possible. However, we also need to rule out cyclic relationships such as:
a=[b]
b=[c]
c=[a]
If this is the case then I believe you can achieve the required ordering by using a PriorityQueue to order keys by the size of the value set related to the key. As keys are removed from the queue they also have to be removed from any of the related value sets that contain them. Which value sets contain a given key can be recovered from a reverse Map<String, Set<String>> which holds the set of keys that refer to a given value key.
Hopefully some code will make things clearer:
static List<String> orderByRef(Map<String, Set<String>> relationshipMap)
{
final Map<String, Set<String>> relationshipMapCopy = new HashMap<>();
for(String key : relationshipMap.keySet())
relationshipMapCopy.put(key, new HashSet<>(relationshipMap.get(key)));
final Map<String, Set<String>> referencedBy = new HashMap<>();
for(String key : relationshipMap.keySet())
referencedBy.put(key, new HashSet<>());
for (Entry<String,Set<String>> e : relationshipMapCopy.entrySet())
for(String v : e.getValue())
referencedBy.get(v).add(e.getKey());
PriorityQueue<String> pq = new PriorityQueue<>(new Comparator<String>()
{
#Override
public int compare(String k1, String k2)
{
return relationshipMapCopy.get(k1).size() - relationshipMapCopy.get(k2).size();
}
});
pq.addAll(relationshipMap.keySet());
List<String> orderedKeys = new ArrayList<>();
while(!pq.isEmpty())
{
String minKey = pq.poll();
if(!relationshipMapCopy.get(minKey).isEmpty())
{
// cyclic relationship
break;
}
orderedKeys.add(minKey);
for(String refKey : referencedBy.get(minKey))
{
// remove minKey from value set of refKey
relationshipMapCopy.get(refKey).remove(minKey);
// reorder refKey in pq
pq.remove(refKey);
pq.add(refKey);
}
}
return orderedKeys;
}
Note that since we're modifying the relationshipMap by removing keys from value sets we first need to create a deep copy. Also, we can detect the presence of a cyclic relationships by checking that the value set of the min key is empty.
Output:
def []
mno []
stu [def]
ghi [def]
vwx [ghi, mno]
jkl [stu, ghi]
zy0 [jkl]
abc [def, ghi, jkl, mno]
pqr [abc]
Which satisfies the constraint that no key is referenced before it appears in the list.
For input containing a cyclic relationship, eg (z=[y]|y=[]|a=[b]|b=[c]|c=[a]), we get:
y []
z [y]
My code sorts a tree map but it do not print duplicate values it also show one of these duplicate values. Can I change it or no? if no, what can I do instead?
TreeMap<Double,String> hm=new TreeMap<Double,String>(Collections.reverseOrder());
hm.put(0.1,"sara");
hm.put(0.13,"nahla");
hm.put(0.13,"saeed");
hm.put(0.2,"omar");
hm.put(0.5,"olaa");
hm.put(0.5,"noha");
Set set = hm.entrySet();
Iterator i2 = set.iterator();
while(i2.hasNext()) {
Map.Entry me = (Map.Entry)i2.next();
System.out.print(me.getKey() + ": ");
System.out.println(me.getValue());
}
the output is:
0.5: noha
0.2: omar
0.13: saeed
0.1: sara
and I want to be like this:
0.5: noha
0.5: olaa
0.2: omar
0.13: saeed
0.13: nahla
0.1: sara
TreeMap doesnt allow duplicates so instead use LinkedMultiValueMap from
org.springframework.util.LinkedMultiValueMap to store and the then sort it.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
public class ds {
public static void main(String[] args) {
MultiValueMap<Double, String> map = new LinkedMultiValueMap<Double, String>();
map.add(8.9, "g");
map.add(4.6, "h");
map.add(10.5, "a");
map.add(10.5, "b");
map.add(9.6, "c");
map.add(8.6, "d");
map.add(8.6, "e");
map.add(8.0, "f");
map.add(2.8, "i");
MultiValueMap<Double, String> filteredMap = filter(5, map);
System.out.println(filteredMap.toString());
}
public static MultiValueMap<Double, String> filter(int numberOfResults,
MultiValueMap<Double, String> map) {
MultiValueMap<Double, String> result = new LinkedMultiValueMap<Double, String>();
List<Double> keys = new ArrayList<Double>(map.keySet());
Collections.sort(keys, Collections.reverseOrder());
for (Double key : keys) {
if (result.size() <= numberOfResults) {
result.put(key, map.get(key));
} else {
break;
}
}
return result;
}
}
How to compare two maps by their values? I have two maps containing equal values and want to compare them by their values. Here is an example:
Map a = new HashMap();
a.put("foo", "bar"+"bar");
a.put("zoo", "bar"+"bar");
Map b = new HashMap();
b.put(new String("foo"), "bar"+"bar");
b.put(new String("zoo"), "bar"+"bar");
System.out.println("equals: " + a.equals(b)); // obviously false
How should I change the code to obtain a true?
The correct way to compare maps for value-equality is to:
Check that the maps are the same size(!)
Get the set of keys from one map
For each key from that set you retrieved, check that the value retrieved from each map for that key is the same (if the key is absent from one map, that's a total failure of equality)
In other words (minus error handling):
boolean equalMaps(Map<K,V>m1, Map<K,V>m2) {
if (m1.size() != m2.size())
return false;
for (K key: m1.keySet())
if (!m1.get(key).equals(m2.get(key)))
return false;
return true;
}
Your attempts to construct different strings using concatenation will fail as it's being performed at compile-time. Both of those maps have a single pair; each pair will have "foo" and "barbar" as the key/value, both using the same string reference.
Assuming you really want to compare the sets of values without any reference to keys, it's just a case of:
Set<String> values1 = new HashSet<>(map1.values());
Set<String> values2 = new HashSet<>(map2.values());
boolean equal = values1.equals(values2);
It's possible that comparing map1.values() with map2.values() would work - but it's also possible that the order in which they're returned would be used in the equality comparison, which isn't what you want.
Note that using a set has its own problems - because the above code would deem a map of {"a":"0", "b":"0"} and {"c":"0"} to be equal... the value sets are equal, after all.
If you could provide a stricter definition of what you want, it'll be easier to make sure we give you the right answer.
To see if two maps have the same values, you can do the following:
Get their Collection<V> values() views
Wrap into List<V>
Collections.sort those lists
Test if the two lists are equals
Something like this works (though its type bounds can be improved on):
static <V extends Comparable<V>>
boolean valuesEquals(Map<?,V> map1, Map<?,V> map2) {
List<V> values1 = new ArrayList<V>(map1.values());
List<V> values2 = new ArrayList<V>(map2.values());
Collections.sort(values1);
Collections.sort(values2);
return values1.equals(values2);
}
Test harness:
Map<String, String> map1 = new HashMap<String,String>();
map1.put("A", "B");
map1.put("C", "D");
Map<String, String> map2 = new HashMap<String,String>();
map2.put("A", "D");
map2.put("C", "B");
System.out.println(valuesEquals(map1, map2)); // prints "true"
This is O(N log N) due to Collections.sort.
See also:
Collection<V> values()
To test if the keys are equals is easier, because they're Set<K>:
map1.keySet().equals(map2.keySet())
See also:
Set<K> keySet()
All of these are returning equals. They arent actually doing a comparison, which is useful for sort. This will behave more like a comparator:
private static final Comparator stringFallbackComparator = new Comparator() {
public int compare(Object o1, Object o2) {
if (!(o1 instanceof Comparable))
o1 = o1.toString();
if (!(o2 instanceof Comparable))
o2 = o2.toString();
return ((Comparable)o1).compareTo(o2);
}
};
public int compare(Map m1, Map m2) {
TreeSet s1 = new TreeSet(stringFallbackComparator); s1.addAll(m1.keySet());
TreeSet s2 = new TreeSet(stringFallbackComparator); s2.addAll(m2.keySet());
Iterator i1 = s1.iterator();
Iterator i2 = s2.iterator();
int i;
while (i1.hasNext() && i2.hasNext())
{
Object k1 = i1.next();
Object k2 = i2.next();
if (0!=(i=stringFallbackComparator.compare(k1, k2)))
return i;
if (0!=(i=stringFallbackComparator.compare(m1.get(k1), m2.get(k2))))
return i;
}
if (i1.hasNext())
return 1;
if (i2.hasNext())
return -1;
return 0;
}
This question is old, but still relevant.
If you want to compare two maps by their values matching their keys, you can do as follows:
public static <K, V> boolean mapEquals(Map<K, V> leftMap, Map<K, V> rightMap) {
if (leftMap == rightMap) return true;
if (leftMap == null || rightMap == null || leftMap.size() != rightMap.size()) return false;
for (K key : leftMap.keySet()) {
V value1 = leftMap.get(key);
V value2 = rightMap.get(key);
if (value1 == null && value2 == null)
continue;
else if (value1 == null || value2 == null)
return false;
if (!value1.equals(value2))
return false;
}
return true;
}
Since you asked about ready-made Api's ... well Apache's commons. collections library has a CollectionUtils class that provides easy-to-use methods for Collection manipulation/checking, such as intersection, difference, and union.
I don't think there is a "apache-common-like" tool to compare maps since the equality of 2 maps is very ambiguous and depends on the developer needs and the map implementation...
For exemple if you compare two hashmaps in java:
- You may want to just compare key/values are the same
- You may also want to compare if the keys are ordered the same way
- You may also want to compare if the remaining capacity is the same
... You can compare a lot of things!
What such a tool would do when comparing 2 different map implementations such that:
- One map allow null keys
- The other throw runtime exception on map2.get(null)
You'd better to implement your own solution according to what you really need to do, and i think you already got some answers above :)
If you assume that there can be duplicate values the only way to do this is to put the values in lists, sort them and compare the lists viz:
List<String> values1 = new ArrayList<String>(map1.values());
List<String> values2 = new ArrayList<String>(map2.values());
Collections.sort(values1);
Collections.sort(values2);
boolean mapsHaveEqualValues = values1.equals(values2);
If values cannot contain duplicate values then you can either do the above without the sort using sets.
The result of equals in your example is obviously false because you are comparing the map a with some values in it with an empty map b (probably a copy and paste error). I recommend to use proper variable names (so you can avoid these kinds of errors) and make use of generics, too.
Map<String, String> first = new HashMap<String, String>();
first.put("f"+"oo", "bar"+"bar");
first.put("fo"+"o", "bar"+"bar");
Map second = new HashMap();
second.put("f"+"oo", "bar"+"bar");
second.put("fo"+"o", "bar"+"bar");
System.out.println("equals: " + first.equals(second));
The concatenation of your strings doesn't have any effect because it will be done at compile time.
#paweloque For Comparing two Map Objects in java, you can add the keys of a map to list and with those 2 lists you can use the methods retainAll() and removeAll() and add them to another common keys list and different keys list. Using the keys of the common list and different list you can iterate through map, using equals you can compare the maps.
The below code will give output like this:
Before {zoo=barbar, foo=barbar}
After {zoo=barbar, foo=barbar}
Equal: Before- barbar After- barbar
Equal: Before- barbar After- barbar
package com.demo.compareExample
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
public class Demo
{
public static void main(String[] args)
{
Map<String, String> beforeMap = new HashMap<String, String>();
beforeMap.put("foo", "bar"+"bar");
beforeMap.put("zoo", "bar"+"bar");
Map<String, String> afterMap = new HashMap<String, String>();
afterMap.put(new String("foo"), "bar"+"bar");
afterMap.put(new String("zoo"), "bar"+"bar");
System.out.println("Before "+beforeMap);
System.out.println("After "+afterMap);
List<String> beforeList = getAllKeys(beforeMap);
List<String> afterList = getAllKeys(afterMap);
List<String> commonList1 = beforeList;
List<String> commonList2 = afterList;
List<String> diffList1 = getAllKeys(beforeMap);
List<String> diffList2 = getAllKeys(afterMap);
commonList1.retainAll(afterList);
commonList2.retainAll(beforeList);
diffList1.removeAll(commonList1);
diffList2.removeAll(commonList2);
if(commonList1!=null & commonList2!=null) // athough both the size are same
{
for (int i = 0; i < commonList1.size(); i++)
{
if ((beforeMap.get(commonList1.get(i))).equals(afterMap.get(commonList1.get(i))))
{
System.out.println("Equal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
}
else
{
System.out.println("Unequal: Before- "+ beforeMap.get(commonList1.get(i))+" After- "+afterMap.get(commonList1.get(i)));
}
}
}
if (CollectionUtils.isNotEmpty(diffList1))
{
for (int i = 0; i < diffList1.size(); i++)
{
System.out.println("Values present only in before map: "+beforeMap.get(diffList1.get(i)));
}
}
if (CollectionUtils.isNotEmpty(diffList2))
{
for (int i = 0; i < diffList2.size(); i++)
{
System.out.println("Values present only in after map: "+afterMap.get(diffList2.get(i)));
}
}
}
/**getAllKeys API adds the keys of the map to a list */
private static List<String> getAllKeys(Map<String, String> map1)
{
List<String> key = new ArrayList<String>();
if (map1 != null)
{
Iterator<String> mapIterator = map1.keySet().iterator();
while (mapIterator.hasNext())
{
key.add(mapIterator.next());
}
}
return key;
}
}
public boolean equalMaps(Map<?, ?> map1, Map<?, ?>map2) {
if (map1==null || map2==null || map1.size() != map2.size()) {
return false;
}
for (Object key: map1.keySet()) {
if (!map1.get(key).equals(map2.get(key))) {
return false;
}
}
return true;
}
If anyone is looking to do it in Java 8 streams below is the example.
import java.util.HashMap;
import java.util.Map;
public class CompareTwoMaps {
public static void main(String[] args) {
Map<String, String> a = new HashMap<>();
a.put("foo", "bar" + "bar");
a.put("zoo", "bar" + "bar");
Map<String, String> b = new HashMap<>();
b.put(new String("foo"), "bar" + "bar");
b.put(new String("zoo"), "bar" + "bar");
System.out.println("result = " + areEqual(a, b));
}
private static boolean areEqual(Map<String, String> first, Map<String, String> second) {
return first.entrySet().stream()
.allMatch(e -> e.getValue().equals(second.get(e.getKey())));
}
}
If you want to compare two Maps then, below code may help you
(new TreeMap<String, Object>(map1).toString().hashCode()) == new TreeMap<String, Object>(map2).toString().hashCode()