Compare two Maps in Java - java

I want to be able to compare two arrays (which will hold values of the same XML but different ages, so i can see if any changed have been made). I have two arrays, one contains attributes and values of the older XML line that i have parsed, and the other contains attributes and latest version of that same XML line that in have parsed.
Example:
Array1:
rect x="220.00"
width="300.00"
id="rect_1"
y="180.00"
height="280.00"
Array2:
rect x="300.00"
width="400.00"
id="rect_1"
height="280.00"
info = "description"
etc etc
So here, the changes would be:
The rect x attribute has changed from 220 (array1) to 300 (array2)
the width attribute has changed from 300 (array1) to 400(array2)
Array2 has gained an attribute called info
y has been removed from array2
How would I compare two arrays and display results like that? Basically I want it to show changes and differences.
Heres the code i tried:
Collection<String> listOne = Arrays.asList(array1);
Collection<String> listTwo = Arrays.asList(array);
Collection<String> similar = new HashSet<String>( listOne );
Collection<String> different = new HashSet<String>();
different.addAll( listOne );
different.addAll( listTwo );
similar.retainAll( listTwo );
different.removeAll( similar );
resultsBuff.append("\nDifferences: \n"+ different + "\n\nChanges: \n" + similar);
This code didn't quite do what I wanted it to do (as described earlier).

You have no choice but to loop through both Arrays.
I would loop through the attributes, split key and value and build a HashMap for each array.
Map<String, String> map1 = new HashMap<String, String>()
for (String attribute : array1) {
String[] splitted = attribute.split("=");
map1.put(splitted[0], splitted[1]);
}
Do the same to create map2.
Map<String, String> map2 = new HashMap<String, String>();
...
Loop through first map and verify if key/value is different or exist in map2 to detected attribute removal.
for (String key : map1.keySet()) {
if (!map2.containsKey(key)) {
System.out.println(key + "has been removed from Array2" )
} else if (!map1.get(key).equals(map2.get(key)) {
System.out.println(key + "attribute has changed from " + map1.get(key) + " to " + map2.get(key) );
}
}
Loop through map2 to detect new attributes
for (String key : map2.keySet()) {
if (!map1.containsKey(key)) {
System.out.println(key + "has been added to Array2" );
}
Hope this helps!

I would use a HashMap instead of an array, because it is better suited for this kind of key/value structures:
map.put("rect x","220.00");
map.put("width","300.00");
...
Build 2 hashmaps from the 2 arrays, and compare them:
if(!map1.equals(map2)) { //something has changed
//loop over keys and compare values
}

I would create an object holding this information and implement a custom equals. You dont need to use an array as a data structure. You can use an object.
For example
public class MyObject{
private double rect_x;
private double width;
private double id;
private double y;
private double height
//usual getters and setters
//implement hashcode
//implemement equals eg
public boolean equals (Object o) {
if (!(o instanceof MyObject)){
return false;
}
MyObject that= MyObject.class.cast(o);
return this.width == that.width && this.id == that.id etc etc
}
}

You already have maps? or they are just arrays?
I mean, are those "labels" implicit or not?
If they're not and you actually have two Map, you could easily do something like:
for(Map.Entry<String,String> pair : array1){
String key = pair.getKey();
String value = pair.getValue();
if(!array2.containsKey(key)){
System.out.println("Array2 lost attribute " + key);
}else{
String value2 = array2.get(key);
if(!value.equals(value2){
System.out.println("The '"+key+"' attribute has changed from "+value+" to "+ value2 ;
}
array2.remove(key);
}
}
for(Map.Entry<String,String> pair : array2){
String key = pair.getKey();
System.out.println("Array2 gained attribute " + key);
}
If you don't have the explicit labels, you just create another mapping before this code and use it to build two maps....

Related

Read objects content from hashmaps

I need to save pairs (string,object) into a hashmap. Basically I managed to populate the hashmap but I don't know how to access the values stored into memory.
This is my code:
HashMap<String, speedDial> SDs = new HashMap<String, speedDial>();
speedDial sd = new speedDial();
SDs.put(String.valueOf(temp),sd); whre temp is my index and sd my object
Then I fill in data into the sd reading them from an xml file.
When I debug the project with eclypse I can see the values are stored correctly into memory, but I've no idea how to retrive the string values associated to the object, see below the SD object format
class speedDial{
String label, dirN;
speedDial (String l, String dN) {
this.label = l;
this.dirN = dN;
}
}
See the picture below: it highlights the data I'm trying to access!
enter image description here
When I try to access the hashmap and print it's values I only got the last one, I use the following:
for ( int k = 0; k <50; k++) {
speedDial.printSD(SDs.get(String.valueOf(k)));
}
This is my printSD method taken from the speedDial class:
public static void printSD (speedDial SD) {
System.out.println("Dir.N: " + SD.dirN + " Label: " + SD.label);
}
And this is the output for all the 50 iterations, that is the last element I added to the hashmap in another for cycle that reads from a xml file.
Dir.N: 123450 Label: label5
Given a HashMap such as:
SpeedDial speedDial1 = new SpeedDial("test1", "test2");
SpeedDial speedDial2 = new SpeedDial("test3", "test4");
SpeedDial speedDial3 = new SpeedDial("test5", "test6");
HashMap<String, SpeedDial> exampleHashMap = new HashMap<>(3);
exampleHashMap.put("key1", speedDial1);
exampleHashMap.put("key2", speedDial2);
exampleHashMap.put("key3", speedDial3);
You can retrieve the value for a given key like so:
SpeedDial exampleOfGetValue = exampleHashMap.get("key1");
System.out.println(exampleOfGetValue.label);
System.out.println(exampleOfGetValue.dirN);
This outputs:
test1
test2
If you want to retrieve the keys for a given value then you could use something like:
public final <S, T> List<S> getKeysForValue(final HashMap<S, T> hashMap, final T value) {
return hashMap.entrySet()
.stream()
.filter(entry -> entry.getValue().equals(value))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
If you call this function like so:
List<String> exampleOfGetKeys = getKeysForValue(exampleHashMap, speedDial1);
System.out.println(exampleOfGetKeys);
It would output a list of all keys that have this value:
[key1]
The following code will iterate through the map and will store the key and values in two lists.
List<String> keys = new ArrayList();
List<Object> values = new ArrayList();
for (Map.Entry<String, Object> speedDial: SDs.entrySet()) {
Object speedDialValue = speedDial.getValue();
String key= speedDial.getKey();
keys.add(key);
values.add(speedDialValue);
}
To retrieve the String value, typically getters are used as it is recommended to use the private modifier for your class attributes.
public class speedDial{
private String label, dirN;
public speedDial (String l, String dN) {
this.label = l;
this.dirN = dN;
}
public String getLabel(){
return this.label;
}
public String getDirN(){
return this.dirN;
}
}
The you can simply use yourObject.getLabel(); or yourObject.getDirN();
Hope that helps!
SDs.keySet() Gives you the Set of the keys of your HashMap
You can have the list of values using
for (String mapKey : SDs.keySet()) {
System.out.println("key: "+mapKey+" value: "+ SDs.get(mapKey).toString());
}
Yous have to write a toString() fonction for your speedDial

List and HashMap group

I just had an issue that I couldn't solve and I would like your opinion.
I have reached at a stage after reading some input files where I have an Array List of objects which includes a number and a color and from the other side I have a HashMap which includes these numbers with names associated.What I want to do is compare the numbers in these two collections and in the end group them by name.That's what I have at the moment and the output I want to succeed.
INPUT
HashMap names
1-Bill
2–John
3-Jason
4-Jack
5-Michael
6-Chris
ArrayList numbers
2-red
3-yellow
1-green
2-pink
2-gold
1-pink
4-brown
DESIRED OUTPUT
Bill [ green , pink ]
John[ red , pink , gold ]
Jason[ blue , red ]
Jack [ brown ]
I have written this code:
public Map<String,String> getAllDetails(){
HashMap<String , String> theEnd =new HashMap<String ,String>();
for (Numbers t : numbers ) {
String plate = t.getNumber();
for (Map.Entry<String, String> entry : names.entrySet()) {
String key = entry.getKey();
if(plate.equals(key)) {
theEnd.put( t.getName() ,entry.getValue());
}
}
}
return theEnd;
}
And the result I get is Pink = Bill , gold = John , red = Jason ,brown=Jack
So for each color I get a name instead of getting all the colors for each name.
What can I do to get all the colors and group them by name??
Thank you very much in advance.
I think you should use a Map<String,String[]> or Map<String,ArrayList<String>> and for each name (String) you create a list (ArrayList<String> or String[]) and you add the colors to the created list.
Your program has logically problem. Do change like
Convert map value as List like HashMap<String , List<String>> theEnd =new HashMap<String ,List<String>>
First loop should be on names, than on numbers. You need to check for each person has key i.e.1 significant how may color. (numbers).
Add inner number object values into List<String>
After complete looping, do looping on theEnd map and desplay values.
Finally theEnd has value each member respectively colors.
You should try to find names for your classes/variables/methods that means something for the domain at hand. For instance theEnd is not really explicit. Technically, the method getAllDetails does a join of 2 collections on the "plate number" key.
Anyway, here is a commented solution to your problem :
As I understand, you have some class Number that contains a "plate" number and a color. Depending on what you intend to do with this bean class, you should complete with equals and hashcode methods.
public class Number {
String plate;
String color;
public Number(String plate, String color) {
this.plate = plate;
this.color = color;
}
public String getPlate() {
return plate;
}
public String getColor() {
return color;
}
}
I suppose numbers and names are collections declared in global scope for getAllDetails.
List<Number> numbers = new ArrayList<>();
Map<String, String> names = new HashMap<>();
The result of getAllDEtails is a Map whose key is a String that holds the name, and the value is a Set of String to hold the colors.
public static Map<String, Set<String>> getAllDetails()
{
// No duplicate of color for each name => use a Set
Map<String , Set<String>> theEnd = new HashMap<>();
for (Number t : numbers) {
String plate = t.getPlate();
// find name associated with plate
String name = names.get(plate);
if (name != null) {
// find if the corresponding name exists in result map
Set<String> colors = theEnd.get(name);
// If the name is not already there create the Set and put the entry
if (colors == null) {
colors = new HashSet<>();
theEnd.put(name, colors);
}
// Add the color to the Set
colors.add(t.getColor());
}
}
return theEnd;
}
And you can test this with :
public static void main(String [] args) {
numbers.add(new Number("2", "red"));
numbers.add(new Number("3", "blue"));
numbers.add(new Number("2", "gold"));
numbers.add(new Number("1000", "black"));
names.put("2", "Joe");
names.put("3", "Mickael");
System.out.println(getAllDetails());
}
It gives : {Joe=[red, gold], Mickael=[blue]}
I'm changing your map and list names just to make it easier to read, although your Numbers class is little clueless because you call the property as name but you actually mean a color name, I'm not changing that now but I suggest you change that as well :)
If you're on JDK7 or above, you can use diamond operator skipping type definition on right hand side, something like this
Map<String,String> namesMap = new HashMap<>();
List<Numbers> numbers = new ArrayList<>();
coming to your getAllDetails(), the problem with your code is that your Map values are of type String but you actually want to capture a list of color strings, so change your theEnd map like below.
Map<String , List<String>> theEnd =new HashMap<>();
/* As you want a map( name, list(colors) ), start iterating on map
so that it's easy to read your logic. Also iterate on keySet
of numbers rather than on entries */
for(String num : namesMap.keySet()){
// get the person name for a number
String name = namesMap.get(num);
for(Numbers number : numbers){
// get the number from each Numbers object
String plate = number.getNumber();
if(plate.equals(num)){
// if numbers match get the colorsList by name
List<String> colorList = theEnd.get(name);
/*first time you find a number, colorsList
will be null, so initialize it to empty list*/
if( colorList == null) {
colorList = new ArrayList<String>();
}
colorList.add(number.getName());
/* after adding your color to colorsList,
update the entry in map again */
theEnd.put(name, colorList);
}
}
}
// Finally print your values
for(String person : theEnd.keySet()){
System.out.println(person+"->"+theEnd.get(person));
}
With this, for your input, I got the following output
Bill->[green, pink]
John->[red, pink, gold]
Jason->[yellow]
Jack->[brown]

storing multiple values in a map against single key

I have the following file named ght.txt in my c: and it contains the following data
Id|ytr|yts
1|W|T
2|W|T
3|W|T
Now the thing is that positions of this columns (Id|ytr|yts) is also not in order means they can be reshuffled also..for ex
Id|ytr|dgfj|fhfjk|fgrt|yts
or they can be as ..
Id|wer|ytr|weg|yts
so I have done the following way and read them in java as shown below
String[] headers = firstLine.split("|");
int id, Ix, Ixt, count = 0;
for(String header : headers) {
if(header.equals("Id")) {
idIx = count;
}elseif (header.equals("Ix")) {
Ixt = count;
} elseif (header.equals("Ixt")) {
Ixt = count;
}
count++;
}
Now I need to store them in a map in such a way that against id I will get the value of column ytr and yts so in map there should be single key but against that key value could be multiple please advise how to store in map in such a way
Using a Map<Integer,List<String>> sounds like a viable first approach.
As it sound like your value is structured, it might be even better to create a value class to hold this, eg. Map<Integer, YourValueClass> where
class YourValueClass
{
String ix;
String ixt;
// constructor, getters and setters
}
Basically, you should think in terms of classes/objects - don't be in object denial :-)
Cheers,
I'm not quite sure what you mean, but if I get it right, you are looking for a multimap.
You can roll one yourself, as #Anders R. Bystrup suggests.
Or you can use an existing implementation like the Google Collections Multimap.
Don't store one key and multiple values. Instead, you can store a Key and Values as a List.
You can use MultiMap from Guava Library:
MultiMap<String,String> map = ArrayListMultimap.create();
map.put("key","value1");
map.put("key","value2");
By using:
System.out.println(map.get("key");
Prints:
["value1","value2"]
Value Class
class TextValues {
final int id;
final String ix;
final String ixt;
private TextValues(final int id, final String ix, final String ixt){
this.id = id;
this.ix = ix;
this.ixt = ixt;
}
public static TextValues createTextValues(int id, String ix, String ixt) {
return new TextValues(id, ix, ixt);
}
}
Usage:
Map<Integer, TextValues> map = new HashMap<Integer, TextValues>();
map.put(1, TextValues.createTextValues(1, "ix value ", "ixt value"));
public static void main(String[] args) {
Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> valSetOne = new ArrayList<String>();
valSetOne.add("ABC");
valSetOne.add("BCD");
valSetOne.add("DEF");
List<String> valSetTwo = new ArrayList<String>();
valSetTwo.add("CBA");
valSetTwo.add("DCB");
map.put("FirstKey", valSetOne);
map.put("SecondKey", valSetTwo);
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
String key = entry.getKey();
List<String> values = entry.getValue();
System.out.println("Value of " + key + " is " + values);
}
}
You can use Set or List based on your requirement i.e you need elements in ordered or unordered collection.This is a simple method of having single key with multiple values.

How to Sort a Map<String, List<Object>> by the Key with the most values (that are not numeric) assigned to it

I have been working with Maps at present and I am baffled by how I can get my program to work effectively. I can iterate over the map get the keys and values and sort them in alphabetical and reverse alphbetical order quite easily and have used custom comparators for this. However, I am now trying to sort the map based on the key with the most values. The values are a list of objects I have created and can be thought of as this scenario.
There is an Atlas(like a catalog) that has lots of towns (the key of type string). That contains Shops(List). I want to sort this so that the town with the most shops is displayed first and goes in descending order with the secondary sorting being based on town alphabetically and return a string representing this.
I have used the Comparator interface with seperate classes for each one alphabetically and reverse alphabetically so far and wish to follow the same pattern for learning purposes However this has me completely stumped.
Example:
class Atlas {
Map<String, List<Shop> atlas = new HashMap<String, List<Shop>();
void addShop(Shop shop){
//if(Atlas already contains){
get the town and add the shop to it.
}
else{
add the town as the key and the shop as the value in the list
}
}
List<Shop> getAllShopsFromTheGivenTown(String givenTown){
//if(Atlas contains givenTown){
return the givenTown from the List.
}
else{
//Return an ArrayList emptyList
}
}
public String returnAllTownsAndShopsAlphbetically(){
String tmpString = "";
List<String> keys = new LinkedList<String>(atlas.keySet());
TownComparatorAtoZ tc = new TownComparatorAtoZ();
Collections.sort(keys, tc);
for(String town : keys){
List<Shop> shops = new LinkedList<Dealer>(atlas.get(town));
ShopComparatorAtoZ sc = new ShopComparatorAtoZ();
Collections.sort(shop, sc);
for(Shop shop : shops){
if(tmpString.isEmpty()){
tmpString = tmpString + town + ": " + shop.getName();
}
else if(tmpString.contains(town)){
tmpString = tmpString + ", " + shop.getName();
}
else{
tmpString = tmpString + " | " + town + ": " + shop.getName(); }
}
}
return tmpString;
}
}
As can be seen from above (although not the cleanest and most efficient) returns things alphabetically and will be reformatted into a string builder. However, I am wondering how I can use a comparator to achieve what I am after and if someone could provide a code snippet with an explanation of what it actually does I would be grateful as its more about understanding how to do it not just getting a copy and pasted lump of code but need to see if visually in code to understand it.
SO output I want to be something like
manchester: m&s, h&m, schuch | birmingham: game, body shop | liverpool: sports
You can try something like this:
public static Map<String, List<Shop>> mySortedMap(final Map<String, List<Shop>> orig)
{
final Comparator<String> c = new Comparator<String>()
{
#Override
public int compare(final String o1, final String o2)
{
// Compare the size of the lists. If they are the same, compare
// the keys themsevles.
final int sizeCompare = orig.get(o1).size() - orig.get(o2).size();
return sizeCompare != 0 ? sizeCompare : o1.compareTo(o2);
}
}
final Map<String, List<Shop>> ret = new TreeMap<String, List<Shop>>(c);
ret.putAll(orig);
return ret;
}
Explanation: TreeMap is the basic implementation of a SortedMap, and it can take a comparator of key values as an argument (if no comparator is passed as an argument, natural ordering of the keys prevails). Here we create an ad hoc comparator comparing the list sizes of the original map passed as an argument, and if the sizes are equal, it compares the keys themselves. Finally, we inject all elements from the origin map into it, and return it.
What if you try something like the following:
private static final Comparator<Map.Entry<String, List<Shop>>> CountThenAtoZ =
new Comparator<Map.Entry<String, List<Shop>>>() {
#Override
public int compare(Map.Entry<String, List<Shop>> x, Map.Entry<String, List<Shop>> y) {
// Compare shop count first. If equal, compare keys alphabetically.
int cmp = ((Integer)x.getValue().size()).compareTo(y.getValue().size());
return cmp != 0 ? cmp : x.getKey().compareTo(y.getKey());
}
};
...
public String returnAllTownsAndShopsAlphbetically() {
List<Map.Entry<String, List<Shop>>> entries = new ArrayList<>(atlas.entrySet());
Collections.sort(entries, CountThenAtoZ);
String result = "";
boolean firstTown = true;
for (Map.Entry<String, List<Shop>> entry : entries) {
if (!firstTown) result += " | "; else firstTown = false;
result += entry.getKey() + ": ";
boolean firstShop = true;
TreeSet<Shop> sortedShops = new TreeSet<>(new ShopComparatorAtoZ());
sortedShops.addAll(entry.getValue());
for (Shop shop : sortedShops) {
if (!firstShop) result += ", "; else firstShop = false;
result += shop.getName();
}
}
return result;
}
The way this works is to first create a list of the atlas entries in exactly the order we want. We need access to both the keys and their associated values to build the correct ordering, so sorting a List of Map.Entry instances is the most convenient.
We then walk the sorted list to build the resulting String, making sure to sort the shops alphabetically before adding them to the String.

Is there an object like a "Set" that can contain only unique string values, but also contain a count on the number of occurrences of the string value?

In Java is there an object like a "Set" that can contain only unique string values, but also contain a count on the number of occurrences of the string value?
The idea is simple
With a data set ala...
A
B
B
C
C
C
I'd like to add each line of text to a Set-like object. Each time that a non-unique text is added to the set I'd like to also have a numeric value associated with the set to display how many times it was added. So if I ran it on the above data set the output would be something like:
A : 1
B : 2
C : 3
any ideas?
You want a "Bag", like the Bag in Apache Commons Collections or the Multiset in Google Collections. You can add the same value to it multiple times, and it'll record the counts of each value. You can then interrogate the counts.
You'd do something like this with Apache Commons' Bag:
Bag myBag = new HashBag();
myBag.add("Orange");
myBag.add("Apple", 4);
myBag.add("Apple");
myBag.remove("Apple", 2);
int apples = myBag.getCount("Apple"); // Should be 3.
int kumquats = myBag.getCount("Kumquat"); // Should be 0.
And this with Google Collections' Multiset.
Multiset<String> myMultiset= HashMultiset.create();
myMultiset.add("Orange");
myMultiset.add("Apple", 4);
myMultiset.add("Apple");
myMultiset.remove("Apple", 2);
int apples = myMultiset.count("Apple"); // 3
int kumquats = myMultiset.count("Kumquats"); // 0
The problem with Apache Collections in general is that it isn't being very actively maintained, and it doesn't yet support Java Generics. To step into this gap, Google's written their own Collections which are extremely powerful. Be sure to evaluate Google Collections first.
Update: Google Collections also offers Multimap, a "collection similar to a Map, but which may associate multiple values with a single key".
Map<String, Integer> would be the best bet, to put in words what you want to do is to Map the amount of occurrences of a string. Basically have something like this:
public void add(String s) {
if (map.containsKey(s)) {
map.put(s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
Yeap, not directly in the core, but can be built easily with a Map.
Here's a naive implementation:
import java.util.Map;
import java.util.HashMap;
public class SetLike {
private Map<String, Integer> map = new HashMap<String,Integer>();
public void add( String s ) {
if( !map.containsKey( s ) ){
map.put( s, 0 );
}
map.put( s, map.get( s ) + 1 );
}
public void printValuesAndCounts() {
System.out.println( map );
}
public static void main( String [] args ){
String [] data = {"A","B","B","C","C","C"};
SetLike holder = new SetLike();
for( String value : data ) {
holder.add( value );
}
holder.printValuesAndCounts();
}
}
Test it
$ javac SetLike.java
$ java SetLike
{A=1, C=3, B=2}
Of course you can improve it much more. You can implement the Set interface, or a List, or a Collection, etc, you can add the iterators, implement Iterable and so on, it depends on what you want and what you need.
This will be helpful..
List<String> myList=new ArrayList<String>();
myList.add("A");
myList.add("B");
myList.add("B");
myList.add("C");
myList.add("C");
myList.add("C");
Set<String> set=new HashSet<String>(myList);
for (String value : set)
{
int occurance=Collections.frequency(myList, value);
System.out.println(value +" occur "+occurance + " times ");
}
Result :
A occur 1 times
B occur 2 times
C occur 3 times

Categories