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]
Related
I want to build up a map based on 2 arrays where 1 key has many objects inside it.
Key: "Letter A" Value: "Albatross"
Value: "Alligator"
Key: "Letter B" Value: "Badger"
Value: "Bandicoot"
The structure must show the key 1 time, without repetitions
Hope the code is self explanatory.
Java 7:
public Map<String, List<String>> group(String[] input) {
Map<String, List<String>> result = new HashMap<>();
for (String str : input) {
String key = "Letter " + str.charAt(0);
if (result.containsKey(key)) {
result.get(key).add(str);//if Key already exists, just add this word to existing list.
} else {
List<String> list = new ArrayList<>();
list.add(str);
result.put(key, list); //Otherwise, create a new list and add the new word into the list
}
}
return result;
}
Java 8:
public static Map<String, List<String>> group(String[] input) {
return Arrays.stream(input)
.collect(Collectors.groupingBy(k -> "Letter " + k.charAt(0)));
//Provide the key for how you want to group. In your case it is first character of string.
}
You can use Guava's Mutlimap implementation, however that may not be Java 7 compatible. https://guava.dev/releases/23.0/api/docs/com/google/common/collect/Multimap.html
You can get the same effect by using a List for the values in your map like so:
Map<String, List<String>> map = new HashMap<>();
Then, let's say for each entry you want to add to the map you have the key in key and value in val, add it like so:
List<String> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(val);
I have Multimap, which contains two strings. Example:
1 = [key,car],
2 = [key,blue],
3 = [key,car]
Multimap definition (I am using Guava library):
ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
And this is how I put values in MultiMap:
for (int i = 0; i < list.size(); i++) {
if (i + 1 < list.size()) {
multimap.put(i,(String) list.get(i));
multimap.put(i,(String) list.get(i+1));
} else if (i + 1 == list.size()) {
}
}
I want to count the occurrences of the same value inside the multimap.
So the result should be 2 if i count how many values [key,car] are (per example I have given above) in my multimap:
occurrences of [key,car] = 2
occurrences of [key,blue] = 1
I have also tried to implement this with multi value HashMap and I was counting it with this, way (Storage is class where I store two string values inside object):
B = Collections.frequency(new ArrayList<Storage>(map.values()), map.get(number));
But I don't get the right results.
You can achieve what you want by creating a map that has your multimap values as the keys and the count as the value:
Map<Collection<String>, Long> result = map_multi.asMap().values().stream()
.collect(Collectors.groupingBy(v -> v, Collectors.counting()));
Here I've used Guava's Multimap.asMap method to get a view over the original multimap, then collected the values into a new map.
Another way, without streams:
Map<Collection<String>, Integer> result = new HashMap<>();
map_multi.asMap().values().forEach(v -> result.merge(v, 1, Integer::sum));
This uses the Map.merge method to accumulate equal values by counting its occurrences.
Please try this code. map_multi.get(key).size() is your answer.
ListMultimap<Integer, String> map_multi = ArrayListMultimap.create();
map_multi.put(1, "car");
map_multi.put(2, "blue");
map_multi.put(3, "apple");
map_multi.put(1, "car");
for (Integer key : map_multi.keySet()) {
System.out.println(map_multi.get(key).get(0) + " occurances: " + map_multi.get(key).size());
}
Output:
car occurances: 2
blue occurances: 1
apple occurances: 1
So the first step is to create a Map<Integer, List<String>> from your ListMultimap. You can do this in the following way:
Map<Integer, List<String>> collect = map_multi.entries()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
Then let's say you have a List<String> with car and key in it.
Example:
List<String> myList = List.of("key", "car"); // java 9
You just iterate through the values() of the map and check if myList contains all elements from map's lists.
long count = collect.values()
.stream()
.filter(list -> list.containsAll(myList))
.count();
I think you're using wrong collection to store your data. Based on what you wrote, you want a map with integer keys and two-element tuples as values and then use Multiset to count frequencies:
Multiset is a collection that supports order-independent equality, like Set, but may have duplicate elements. A multiset is also sometimes called a bag.
Elements of a multiset that are equal to one another are referred to as occurrences of the same single element. The total number of occurrences of an element in a multiset is called the count of that element (the terms "frequency" and "multiplicity" are equivalent, but not used in this API).
The code below assumes you have proper implementation of two-element tuple (aka pair, or your Storage class, but with proper equals and hashCode implementations), like this one from jOOL:
HashMap<Integer, Tuple2<String, String>> m = new HashMap<>();
Tuple2<String, String> carTuple = new Tuple2<>("key", "car");
Tuple2<String, String> blueTuple = new Tuple2<>("key", "blue");
m.put(1, carTuple);
m.put(2, blueTuple);
m.put(3, carTuple);
ImmutableMultiset<Tuple2<String, String>> occurrences =
ImmutableMultiset.copyOf(m.values());
System.out.println(occurrences); // [(key, car) x 2, (key, blue)]
If you need to have few values (tuples) mapped under one key (integer), then you should change the first line to multimap:
ListMultimap<Integer, Tuple2<String, String>> m = ArrayListMultimap.create();
so that m.put(1, anotherTuple) is possible and putting doesn't override first value (carTuple) but rather adds it under 1 values list.
EDIT:
You can implement Tuple2 yourself if you don't need/want additional dependency, it could look like this class:
public class Tuple2<T1, T2> {
public final T1 v1;
public final T2 v2;
public Tuple2(T1 v1, T2 v2) {
this.v1 = v1;
this.v2 = v2;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Tuple2)) {
return false;
}
#SuppressWarnings({"unchecked", "rawtypes"}) final Tuple2<T1, T2> that = (Tuple2) o;
return Objects.equals(v1, that.v1) && Objects.equals(v2, that.v2);
}
#Override
public int hashCode() {
return Objects.hash(v1, v2);
}
#Override
public String toString() {
return "(" + v1 + ", " + v2 + ")";
}
}
I'm new to Java, so sorry if this is pretty obvious, but I can't quite understand how to work with 2 HashMaps inside each other
I have my main, where I want to add some words to a Map, and then, I want to read them:
public static void main(String[] args) {
Dicionario d = new Dicionario();
d.add("english", "BOOK", "Book");
d.add("french", "BOOK", "livre");
d.add("portuguese", "BOOK", "livro");
d.add("english", "YEAR", "year");
d.add("french", "YEAR", "an");
d.add("portuguese", "YEAR", "ano");
System.out.println(d);
}
This Map, has another Map inside him:
private Map<String, Map<String, String> > dic = new HashMap<>();
Then I add those words:
protected void add(String s1, String s2, String s3){
Map<String, String> m = new HashMap<>();
m.put(s2, s3);
dic.put(s1, m);
}
And redefine the function toString to read them, but only appears 1 value per key:
#Override
public String toString(){
String s= "";
for(Map.Entry<String, Map<String,String>> entry : dic.entrySet())
{
s += "\"" + entry.getKey() + "\": ";
for(Map.Entry<String, String> entry2 : dic.get(entry.getKey()).entrySet())
{
s+=entry2.getKey() + "->" + entry2.getValue() + "\t";
}
s+="\n";
}
return s;
}
Why is that? I am looking at this like if it was a bidimensional array, but with 2 values (key, value) in each position.
How can I do to show all the values that the keys from the first map have?
Thanks, and sorry for such a basic question.
You need to modify your add method to following
protected void add(String s1, String s2, String s3) {
Map<String, String> m = null;
m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
}
m.put(s2, s3);
dic.put(s1, m);
}
The problem is that in your add(String, String, String) method, you are instancing a new HashMap each time so you overwrite the previously instanced HashMap from a previous call.
You should update your method this way:
protected void add(String s1, String s2, String s3){
Map<String, String> m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
dic.put(s1, m);
}
m.put(s2, s3);
}
To avoid having to manage this by hand yourself, I suggest that you use Guava's Table data structure (more specifically HashBasedTable).
I would like to ask
in Hashmap
how can I count all the numbers if the element ID is same?
Could anyone give some idea for me?
Or just give some useful reference links for me to study
thanks
import java.util.*;
import java.util.Iterator;
public class hash {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("ABS", new Double(3434.34));
hashMap.put("ABD", new Double(123.22));
hashMap.put("ABD", new Double(123.22));
hashMap.put("ABD", new Double(123.22));
hashMap.put("ABD", new Double(123.22));
Set set = hashMap.entrySet();
Iterator i = set.iterator();
while (i.hasNext()) {
Map.Entry me = (Map.Entry) i.next();
System.out.println(me.getKey() + " : " + me.getValue());
}
}
}
You can't. You need a data structure that supports counting keys. Map implementations don't.
Guava
One thing you can use is Guava's Multimap
Sample Code:
final Multimap<String, Double> map =
Multimaps.newListMultimap(
Maps.<String, Collection<Double>>newTreeMap(),
new Supplier<List<Double>>(){
#Override
public List<Double> get() {
return Lists.newArrayList();
}});
map.put("ABS", Double.valueOf(3434.34));
map.put("ABD", Double.valueOf(123.22));
map.put("ABD", Double.valueOf(123.22));
map.put("ABD", Double.valueOf(123.22));
map.put("ABD", Double.valueOf(123.22));
for (final Multiset.Entry<String> key : map.keys().entrySet()) {
System.out.println(
"Key: "
+key.getElement()
+", count: "
+key.getCount()
+", values: "
+map.get(key.getElement())
);
}
Output:
Key: ABD, count: 4, values: [123.22, 123.22, 123.22, 123.22]
Key: ABS, count: 1, values: [3434.34]
The MultiMap in Apache Commons / Collections will also do the trick.
Plain Java
If you are not allowed to use an external library, you can still implement this functionality with a Map<String, List<Double>>. Create a method like this
Helper Method
public static <K, V> void assignValue(
final Map<K, Collection<V>> map, final K key, final V value) {
Collection<V> values = map.get(key);
if(values==null){
values=new ArrayList<V>();
map.put(key, values);
}
values.add(value);
}
Usage
And use it like this:
final Map<String, Collection<Double>> map =
new HashMap<String, Collection<Double>>();
assignValue(map, "ABS", Double.valueOf(3434.34));
assignValue(map, "ABD", Double.valueOf(123.22));
assignValue(map, "ABD", Double.valueOf(123.22));
assignValue(map, "ABD", Double.valueOf(123.22));
assignValue(map, "ABD", Double.valueOf(123.22));
for(final Entry<String, Collection<Double>> entry : map.entrySet()){
System.out.println(new StringBuilder()
.append("Key: ")
.append(entry.getKey())
.append(", count: ")
.append(entry.getValue().size())
.append(", values: ")
.append(entry.getValue())
.toString());
}
Output
Key: ABD, count: 4, values: [123.22, 123.22, 123.22, 123.22]
Key: ABS, count: 1, values: [3434.34]
HashMap is-a Map which cannot contain duplicate keys. If duplicate keys are allowed, which value will you expect to return when calling map.get(duplicateKey)?
Have a read through this, particularly the 'put' method which says:
Associates the specified value with the specified key in this map. If the map previously contained a mapping for this key, the old value is replaced.
The way you've structured your code means that the HashMap will only ever contain two key/value pairs.
public class hash {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("ABS", new Double(3434.34)); // "ABS" key created. 1 object in map.
hashMap.put("ABD", new Double(123.22)); // "ABD" key created. 2 objects in map.
hashMap.put("ABD", new Double(123.22)); // "ABD" reference will point to new value.
hashMap.put("ABD", new Double(123.22)); // .. and again
hashMap.put("ABD", new Double(123.22)); // .. and again
So while you've written five put lines, only two objects will ever be placed in the HashMap.
You can use
hashMap.size()
to return an int value of how many key/value pairs are in 'hashMap'.
so you need to have Double+Count as value then and use it instead of Double, as a exercise make it extend java.lang.Number :)
Class DoubleCount{
final double value;
private int count;
DoubleCount(double value){
this.value = value;
}
public int getCount(){
return count;
}
public double getValue(){
return value;
}
public int incCount(){
return ++count;
}
}
size = hashMap.size();
then use for loop to iterate
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()