Remove duplicate in ArrayList of Custom objects - java

i'm trying to remove duplicate objects from my array.
I have my custom which is made of two double : x and y.
What i want to do is to remove duplicate ( (x && y) == (x1 && y1)) and if x == x1 i want to keep the object which has the higher y.
ArrayList<MyObject> list = [x(0),y(0)], [x(0),y(0)], [x(0.5),y(0.5], [x(0.5),y(0.6)], [x(1),y(1)];
ArrayList<MyObject> results = [x(0),y(0)], [x(0.5),y(0.6)], [x(1),y(1)];
I tried to implement the equals method but i do not how to use it :
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof MyObject)) {
return false;
}
return (this.x == ((MyObject)obj).x);
}
list is always ordered using Collections.sort by x.
Thanks for all.

Given MyObject like this:
class MyObject {
private final double x;
private final double y;
public MyObject(double x, double y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyObject myObject = (MyObject) o;
if (Double.compare(myObject.x, x) != 0) return false;
if (Double.compare(myObject.y, y) != 0) return false;
return true;
}
#Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(x);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
You can implement a unique method that returns a list with unique elements only:
private List<MyObject> unique(List<MyObject> list) {
List<MyObject> uniqueList = new ArrayList<>();
Set<MyObject> uniqueSet = new HashSet<>();
for (MyObject obj : list) {
if (uniqueSet.add(obj)) {
uniqueList.add(obj);
}
}
return uniqueList;
}
And a unit test for it to verify it works:
#Test
public void removeDups() {
List<MyObject> list = Arrays.asList(new MyObject(0, 0), new MyObject(0, 0), new MyObject(0.5, 0.5), new MyObject(0.5, 0.6), new MyObject(1, 1));
List<MyObject> results = Arrays.asList(new MyObject(0, 0), new MyObject(0.5, 0.5), new MyObject(0.5, 0.6), new MyObject(1, 1));
assertEquals(results, unique(list));
}
Note: it's important to implement both equals and hashCode for this to work,
because of the use of a hash map. But you should always do this anyway in your custom classes: provide appropriate equals and hashCode implementations. Btw, I didn't write those equals and hashCode methods. I let my IDE (IntelliJ) generate them automatically from the fields x and y of the class.

Make sure to override equals() method in your custom Object(MyObject).
Then add them into a Set. Now you have unique result set.

Use a Set instead of a List ...
You have to override equals() and hashCode(). The most IDE can generate that for you!
To "convert" a List to a Set you can simply use this:
ArrayList<MyObject> list = ...
Set<MyObject> mySet = new HashSet<MyObject>(list);
Then you have a set with unique elements. You can iterate over the set like this:
for (MyObject o : mySet){
o.getX();
}

The most optimal solution would be if you could use a Set. However, there are two Set implementations in Java: HashSet and TreeSet. HashSet requires that you declare equals and hashCode methods, while TreeSet requires your class to implement Comparable with a compareTo method or supply a Comparator. Neither solution will work in your case because you want to keep the higher y when x is equal. If you sort/calculate equality based on x, y you will have duplicate x, and if you sort/calculate equality based on x only you will get the first x entered, which is not what you want.
Therefore, what we need to do is:
Sort by x ascending, y descending
Convert to Set that preserves original order, but bases equality only on x
Convert back to list (if necessary)
XAscYdesc Comparator Method (Not accounting for nulls):
public int compare(MyObject left, MyObject right) {
int c = left.x - right.x;
if(c != 0) {
return c;
}
return right.y - left.y;
}
XAsc Comparator Method (Not accounting for nulls):
public int compare(MyObject left, MyObject right) {
return left.x - right.x;
}
(Using the Guava library; it's very useful for one-liners like this):
Collections.sort(list, new XAscYdesc());
Lists.newArrayList(ImmutableSortedSet.copyOf(new XAsc(), list));

You need to order your collection on both x and y with x first (think about it as x and y forming an hypothetical number with x on the left side of the decimal point, and y on the right side: when you sort number in growing order, if the integral parts are equal, you sort on the decimal part).
Now, with an equal predicate, it will be difficult to sort values (you can only tell if they are equal, not if one is before another). Instead, you need to implement the comparable interface, with the following method:
public int compare(Object obj) {
if (obj == null || !(obj instanceof MyObject)) {
// raise an Exception
}
MyObject other = (MyObject)obj;
if (x < other.x) return -1;
if (this.x > other.x) return 1;
if (this.y < other.y) return -1;
if (this.y > other.y) return 1;
return 0;
}
If your array is sorted according to this comparison, you just need to keep the last entry with the same x in your array to get what you want. This means that you remove entries unless its successor has a different x.
This method is interesting of you don't want to keep your original data, but only keep the result: it will update the existing array in place, for a complexity of O(n) in time (not counting the sorting, which should happen anyway if I understand your question correctly).
Alternatively, the whole filtering can be achieved by applying a fold on your collection, where folding here is simply keeping the highest y for a same x (as you stated it precisely in your question). Because your collection is already sorted on x, it means that it is naturally partitioned on values of x, so you can build a new collection by accumulating the correct x for each partition in another array. For each element of the array, you compare it with the last inserted entry in your new array, if the x are the same, you replace it with the object with the highest y. If the x are different, you add it to the new array.
The advantage of this approach is that you don't need to change the sorting algorithm, but on the other hand you need a new array. This algorithm should therefore work in O(n) space and time.
Finally, this algorithm may be adapted to an in place update of the original array. It is slightly more complicated, but would let you avoid the extra allocation (crucial on embedded systems).
Pseudo code:
int idx = 0;
while (idx + 1 < Objarray.size()){
MyObj oi = Objarray.get(idx), on = Objarray.get(idx+1);
if (oi.x == on.x) {
if (on.y < oi.y)
Objarray.remove(idx++);
else
Objarray.remove(idx+1);
} else
idx++;
}
Note that while working in constant space, it might be slightly less efficient than the allocating algorithm, because of the way ArrayList works internally (though it should be better than using other usual container types).

/**
*
*/
package test1;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* #author raviteja
*
*/
public class UinquecutomObjects {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee e1=new Employee();
e1.setName("abc");
e1.setNo(1);
Employee e2=new Employee();
e2.setName("def");
e2.setNo(2);
Employee e3=new Employee();
e3.setName("abc");
e3.setNo(1);
List<Employee> empList=new ArrayList<Employee>();
empList.add(e1);
empList.add(e2);
empList.add(e3);
System.out.println("list size is "+empList.size());
Set<Employee> set=new HashSet<Employee>(empList);
System.out.println("set size is "+set.size());
System.out.println("set elements are "+set);
}
}
class Employee{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
private int no;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + no;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (no != other.no)
return false;
return true;
}
}

Related

Java Hash Code Implementation with Multiple equals and if

From what i know, Every equals object must have same hash code. However what if in equals method have multiple if that need to be followed ?
Location is an object, Junction is an object, length is an integer, offset is an integer, section is an object.
I already solved the one when atAJunction method, the hashcode i only used junction as the additional hashcode. And when Section is equal, its section's length is equal to the both location's offset. The hashcode that i used for this is only using section as the hashcode.
The main problem is when they have different section, but same offset and endPoint. its equal but the hashcode is different.
Is there anyone can help with my problem ? Thanks Before. :)
This is my Equals method :
#Override
public boolean equals(Object object) {
if(object == null){
return false;
}
if(!(object instanceof Location)){
return false;
}else{
Location otherLocation = (Location) object;
if(atAJunction() && otherLocation.atAJunction()){
return this.endPoint.getJunction().equals(otherLocation.getEndPoint().getJunction());
}else{
// The Problem Here
if(this.endPoint.equals(otherLocation.endPoint)){
return this.offset == otherLocation.getOffset();
}else{
return this.section.equals(otherLocation.getSection()) &&
this.section.getLength() == (this.offset + otherLocation.getOffset());
}
}
}
}
And this is my Hash Code :
#Override
public int hashCode() {
// creates a polynomial hash-code based on the fields of the class.
final int prime = 13; // an odd base prime
int result = 1; // the hash code under construction
if(atAJunction()){
result = prime * result + this.endPoint.getJunction().hashCode();
}else{
result = prime * result + this.section.hashCode();
}
return result;
}
Firstly what I dont understand is why you need this additional check
return this.section.equals(otherLocation.getSection()) &&
**this.section.getLength() == (this.offset + otherLocation.getOffset());**
Ideally if sections are equal, then length and all should be already covered in it. Comparing internal value of one object with some derived value of other class is error prone, and should be avoided in methods like equals().
You may want to take a look at this book, http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683. The auther here explains the relation in hash-code and equals in a decent way. This would be definitely helpful to you.
In your case, what I would suggest is, if you are not very clear about right way to generate hashcode() and equals(), try using codegenerators inbuilt in IDE's like Eclipse or netbeans.
Please visit another resourse For Ex. below.
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
// ...
}
Than you create Equals and HashCode as in Effective Java
// A better definition, but still not perfect
#Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof Point) {
Point that = (Point) other;
result = (this.getX() == that.getX() && this.getY() == that.getY());
}
return result;
}
#Override public int hashCode() {
return (41 * (41 + getX()) + getY());
}

Key of a HashMap is a pair of integers. How to convert and store it? [duplicate]

I have a 2D array of Integers. I want them to be put into a HashMap. But I want to access the elements from the HashMap based on Array Index. Something like:
For A[2][5], map.get(2,5) which returns a value associated with that key. But how do I create a hashMap with a pair of keys? Or in general, multiple keys: Map<((key1, key2,..,keyN), Value) in a way that I can access the element with using get(key1,key2,...keyN).
EDIT : 3 years after posting the question, I want to add a bit more to it
I came across another way for NxN matrix.
Array indices, i and j can be represented as a single key the following way:
int key = i * N + j;
//map.put(key, a[i][j]); // queue.add(key);
And the indices can be retrevied from the key in this way:
int i = key / N;
int j = key % N;
There are several options:
2 dimensions
Map of maps
Map<Integer, Map<Integer, V>> map = //...
//...
map.get(2).get(5);
Wrapper key object
public class Key {
private final int x;
private final int y;
public Key(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return x == key.x && y == key.y;
}
#Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
Implementing equals() and hashCode() is crucial here. Then you simply use:
Map<Key, V> map = //...
and:
map.get(new Key(2, 5));
Table from Guava
Table<Integer, Integer, V> table = HashBasedTable.create();
//...
table.get(2, 5);
Table uses map of maps underneath.
N dimensions
Notice that special Key class is the only approach that scales to n-dimensions. You might also consider:
Map<List<Integer>, V> map = //...
but that's terrible from performance perspective, as well as readability and correctness (no easy way to enforce list size).
Maybe take a look at Scala where you have tuples and case classes (replacing whole Key class with one-liner).
When you create your own key pair object, you should face a few thing.
First, you should be aware of implementing hashCode() and equals(). You will need to do this.
Second, when implementing hashCode(), make sure you understand how it works. The given user example
public int hashCode() {
return this.x ^ this.y;
}
is actually one of the worst implementations you can do. The reason is simple: you have a lot of equal hashes! And the hashCode() should return int values that tend to be rare, unique at it's best. Use something like this:
public int hashCode() {
return (X << 16) + Y;
}
This is fast and returns unique hashes for keys between -2^16 and 2^16-1 (-65536 to 65535). This fits in almost any case. Very rarely you are out of this bounds.
Third, when implementing equals() also know what it is used for and be aware of how you create your keys, since they are objects. Often you do unnecessary if statements cause you will always have the same result.
If you create keys like this: map.put(new Key(x,y),V); you will never compare the references of your keys. Cause everytime you want to acces the map, you will do something like map.get(new Key(x,y));. Therefore your equals() does not need a statement like if (this == obj). It will never occur.
Instead of if (getClass() != obj.getClass()) in your equals() better use if (!(obj instanceof this)). It will be valid even for subclasses.
So the only thing you need to compare is actually X and Y. So the best equals() implementation in this case would be:
public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}
So in the end your key class is like this:
public class Key {
public final int X;
public final int Y;
public Key(final int X, final int Y) {
this.X = X;
this.Y = Y;
}
public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}
public int hashCode() {
return (X << 16) + Y;
}
}
You can give your dimension indices X and Y a public access level, due to the fact they are final and do not contain sensitive information. I'm not a 100% sure whether private access level works correctly in any case when casting the Object to a Key.
If you wonder about the finals, I declare anything as final which value is set on instancing and never changes - and therefore is an object constant.
You can't have an hash map with multiple keys, but you can have an object that takes multiple parameters as the key.
Create an object called Index that takes an x and y value.
public class Index {
private int x;
private int y;
public Index(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
return this.x ^ this.y;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Index other = (Index) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Then have your HashMap<Index, Value> to get your result. :)
Implemented in common-collections MultiKeyMap
Java 7+ contains a new Map.Entry<K,V> class that you can use as key for your map (or entry for your set). Java 9+ also contains a Map.entry(K k, V v) method to easily create new Map.Entry objects.
Usage:
Map<Map.Entry<Integer,Integer>, Integer> map = new HashMap<>();
map.put(Map.entry(1, 2), 0);
There is also Pair<K, V> in javafx.util
Map<Pair<Integer,Integer>, Integer> map = new HashMap<>();
map.put(new Pair(1, 2), 0);
Two possibilities. Either use a combined key:
class MyKey {
int firstIndex;
int secondIndex;
// important: override hashCode() and equals()
}
Or a Map of Map:
Map<Integer, Map<Integer, Integer>> myMap;
Use a Pair as keys for the HashMap. JDK has no Pair, but you can either use a 3rd party libraray such as http://commons.apache.org/lang or write a Pair taype of your own.
Create a value class that will represent your compound key, such as:
class Index2D {
int first, second;
// overrides equals and hashCode properly here
}
taking care to override equals() and hashCode() correctly. If that seems like a lot of work, you might consider some ready made generic containers, such as Pair provided by apache commons among others.
There are also many similar questions here, with other ideas, such as using Guava's Table, although allows the keys to have different types, which might be overkill (in memory use and complexity) in your case since I understand your keys are both integers.
If they are two integers you can try a quick and dirty trick: Map<String, ?> using the key as i+"#"+j.
If the key i+"#"+j is the same as j+"#"+i try min(i,j)+"#"+max(i,j).
You can also use guava Table implementation for this.
Table represents a special map where two keys can be specified in combined fashion to refer to a single value. It is similar to creating a map of maps.
//create a table
Table<String, String, String> employeeTable = HashBasedTable.create();
//initialize the table with employee details
employeeTable.put("IBM", "101","Mahesh");
employeeTable.put("IBM", "102","Ramesh");
employeeTable.put("IBM", "103","Suresh");
employeeTable.put("Microsoft", "111","Sohan");
employeeTable.put("Microsoft", "112","Mohan");
employeeTable.put("Microsoft", "113","Rohan");
employeeTable.put("TCS", "121","Ram");
employeeTable.put("TCS", "122","Shyam");
employeeTable.put("TCS", "123","Sunil");
//get Map corresponding to IBM
Map<String,String> ibmEmployees = employeeTable.row("IBM");
You could create your key object something like this:
public class MapKey {
public Object key1;
public Object key2;
public Object getKey1() {
return key1;
}
public void setKey1(Object key1) {
this.key1 = key1;
}
public Object getKey2() {
return key2;
}
public void setKey2(Object key2) {
this.key2 = key2;
}
public boolean equals(Object keyObject){
if(keyObject==null)
return false;
if (keyObject.getClass()!= MapKey.class)
return false;
MapKey key = (MapKey)keyObject;
if(key.key1!=null && this.key1==null)
return false;
if(key.key2 !=null && this.key2==null)
return false;
if(this.key1==null && key.key1 !=null)
return false;
if(this.key2==null && key.key2 !=null)
return false;
if(this.key1==null && key.key1==null && this.key2 !=null && key.key2 !=null)
return this.key2.equals(key.key2);
if(this.key2==null && key.key2==null && this.key1 !=null && key.key1 !=null)
return this.key1.equals(key.key1);
return (this.key1.equals(key.key1) && this.key2.equals(key2));
}
public int hashCode(){
int key1HashCode=key1.hashCode();
int key2HashCode=key2.hashCode();
return key1HashCode >> 3 + key2HashCode << 5;
}
}
The advantage of this is: It will always make sure you are covering all the scenario's of Equals as well.
NOTE: Your key1 and key2 should be immutable. Only then will you be able to construct a stable key Object.
we can create a class to pass more than one key or value and the object of this class can be used as a parameter in map.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class key1 {
String b;
String a;
key1(String a,String b)
{
this.a=a;
this.b=b;
}
}
public class read2 {
private static final String FILENAME = "E:/studies/JAVA/ReadFile_Project/nn.txt";
public static void main(String[] args) {
BufferedReader br = null;
FileReader fr = null;
Map<key1,String> map=new HashMap<key1,String>();
try {
fr = new FileReader(FILENAME);
br = new BufferedReader(fr);
String sCurrentLine;
br = new BufferedReader(new FileReader(FILENAME));
while ((sCurrentLine = br.readLine()) != null) {
String[] s1 = sCurrentLine.split(",");
key1 k1 = new key1(s1[0],s1[2]);
map.put(k1,s1[2]);
}
for(Map.Entry<key1,String> m:map.entrySet()){
key1 key = m.getKey();
String s3 = m.getValue();
System.out.println(key.a+","+key.b+" : "+s3);
}
// }
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null)
br.close();
if (fr != null)
fr.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
You can download it from the below link:
https://github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java
https://github.com/VVS279/DoubleKeyHashMap
You can use double key: value hashmap,
DoubleKeyHashMap<Integer, Integer, String> doubleKeyHashMap1 = new
DoubleKeyHashMap<Integer, Integer, String>();
DoubleKeyHashMap<String, String, String> doubleKeyHashMap2 = new
DoubleKeyHashMap<String, String, String>();
Using org.apache.commons.lang3.tuple.Pair is very easy;
Map<String, Pair<String, Integer>> map= new HashMap<>();
map.put("key", Pair.of("a", 1));
int value = map.get("key").getRight();

checking whether two sequences have the same values in the same order (beginning java)

I want to add a method to a class I made that checks whether the two sequences have the same values in the same order.
Here is what I have so far:
public class Sequence {
private int[] values;
public Sequence(int size) { values = new int[size]; }
public void set(int i, int n) { values[i] = n; }
}
public boolean equals (Sequence other)
...??
The first part of the class I think is correct but I'm having a lot of trouble with the method that tests if the values are in the same order. Ideas and feedback would be much appreciated :)
If you want to say wether 2 Sequences are equals, you can override equals method and hashCode to follow contract.
Example using Eclipse tool:
public class Sequence {
private int[] values;
public Sequence(int size) { values = new int[size]; }
public void set(int i, int n) { values[i] = n; }
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(values);
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Sequence other = (Sequence) obj;
if (!Arrays.equals(values, other.values))
return false;
return true;
}
}
Then in a main class you can do the following thing
public static void main(String args[]){
Sequence s = new Sequence(5);
Sequence s2 = new Sequence(5);// new Sequence(4)
s.set(0, 1);
s2.set(0, 1);
System.out.println(s.equals(s2));//will print true
}
You have to take care that if you use my comment code (new Sequence(4)) this will return false and perhaps is not what you want! Then you will have to implement your own equals and not autogenerated by ide.
Arrays have a built in .equals() method: .equals(int[], int[])
Very simple, but hope this helps.
public boolean equals (Sequence other) {
// if sizez are different, automatic false
if (this.getValues().length != other.getValues().length)
return false;
else
int[] array1 = other.getValues();
int[] array2 = this.getValues();
// if any indices are not equal, return false
for (int i = 0; i < other.getValues().length; i++){
if (array1[i] != array2[i])
return false;
}
// it not returned false, return true
return true;
}
First and foremost, you will need to make your .equals() method a member of your Sequence class. Otherwise, you will only have access to one Sequence object.
If you want to check if 2 arrays have the same elements in the same order, all you need to do is compare each element in turn. Is the first element of this one the same as the first element of the other, etc. When you come across a pair of elements that are different, you will be able to return false. Otherwise, you can return true when you have checked each pair of elements.
One issue you may encounter is arrays of different size. Depending on what you're trying to do, you may want to either return false immediately without checking the elements or stop when you reach the end of the shorter array. Based off your question, you probably want the former, but that depends on what problem you are trying to solve.
Your .equals() method will be able to access the values arrays of both this and other even if they aren't public. This is because .equals() as a function of the Sequence class is allowed to access all the members of Sequence, even in Sequence objects other than this.
With this information, you should be able to write your .equals() method.
public boolean equals (Sequence other){
int[] first = this.getValues();
int[] second = other.getValues();
boolean same = true;
if(first.length != second.length){
return false;
}
for(int i = 0; i < first.length; i++){
if(first[i] != second[i]){
return false;
}
}
return same;
}
Note: you will have to make the values array public in the Sequence class or add a getter method to the Sequence class...a getter would be the better option

How to create a HashMap with two keys (Key-Pair, Value)?

I have a 2D array of Integers. I want them to be put into a HashMap. But I want to access the elements from the HashMap based on Array Index. Something like:
For A[2][5], map.get(2,5) which returns a value associated with that key. But how do I create a hashMap with a pair of keys? Or in general, multiple keys: Map<((key1, key2,..,keyN), Value) in a way that I can access the element with using get(key1,key2,...keyN).
EDIT : 3 years after posting the question, I want to add a bit more to it
I came across another way for NxN matrix.
Array indices, i and j can be represented as a single key the following way:
int key = i * N + j;
//map.put(key, a[i][j]); // queue.add(key);
And the indices can be retrevied from the key in this way:
int i = key / N;
int j = key % N;
There are several options:
2 dimensions
Map of maps
Map<Integer, Map<Integer, V>> map = //...
//...
map.get(2).get(5);
Wrapper key object
public class Key {
private final int x;
private final int y;
public Key(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return x == key.x && y == key.y;
}
#Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
Implementing equals() and hashCode() is crucial here. Then you simply use:
Map<Key, V> map = //...
and:
map.get(new Key(2, 5));
Table from Guava
Table<Integer, Integer, V> table = HashBasedTable.create();
//...
table.get(2, 5);
Table uses map of maps underneath.
N dimensions
Notice that special Key class is the only approach that scales to n-dimensions. You might also consider:
Map<List<Integer>, V> map = //...
but that's terrible from performance perspective, as well as readability and correctness (no easy way to enforce list size).
Maybe take a look at Scala where you have tuples and case classes (replacing whole Key class with one-liner).
When you create your own key pair object, you should face a few thing.
First, you should be aware of implementing hashCode() and equals(). You will need to do this.
Second, when implementing hashCode(), make sure you understand how it works. The given user example
public int hashCode() {
return this.x ^ this.y;
}
is actually one of the worst implementations you can do. The reason is simple: you have a lot of equal hashes! And the hashCode() should return int values that tend to be rare, unique at it's best. Use something like this:
public int hashCode() {
return (X << 16) + Y;
}
This is fast and returns unique hashes for keys between -2^16 and 2^16-1 (-65536 to 65535). This fits in almost any case. Very rarely you are out of this bounds.
Third, when implementing equals() also know what it is used for and be aware of how you create your keys, since they are objects. Often you do unnecessary if statements cause you will always have the same result.
If you create keys like this: map.put(new Key(x,y),V); you will never compare the references of your keys. Cause everytime you want to acces the map, you will do something like map.get(new Key(x,y));. Therefore your equals() does not need a statement like if (this == obj). It will never occur.
Instead of if (getClass() != obj.getClass()) in your equals() better use if (!(obj instanceof this)). It will be valid even for subclasses.
So the only thing you need to compare is actually X and Y. So the best equals() implementation in this case would be:
public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}
So in the end your key class is like this:
public class Key {
public final int X;
public final int Y;
public Key(final int X, final int Y) {
this.X = X;
this.Y = Y;
}
public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}
public int hashCode() {
return (X << 16) + Y;
}
}
You can give your dimension indices X and Y a public access level, due to the fact they are final and do not contain sensitive information. I'm not a 100% sure whether private access level works correctly in any case when casting the Object to a Key.
If you wonder about the finals, I declare anything as final which value is set on instancing and never changes - and therefore is an object constant.
You can't have an hash map with multiple keys, but you can have an object that takes multiple parameters as the key.
Create an object called Index that takes an x and y value.
public class Index {
private int x;
private int y;
public Index(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
return this.x ^ this.y;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Index other = (Index) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Then have your HashMap<Index, Value> to get your result. :)
Implemented in common-collections MultiKeyMap
Java 7+ contains a new Map.Entry<K,V> class that you can use as key for your map (or entry for your set). Java 9+ also contains a Map.entry(K k, V v) method to easily create new Map.Entry objects.
Usage:
Map<Map.Entry<Integer,Integer>, Integer> map = new HashMap<>();
map.put(Map.entry(1, 2), 0);
There is also Pair<K, V> in javafx.util
Map<Pair<Integer,Integer>, Integer> map = new HashMap<>();
map.put(new Pair(1, 2), 0);
Two possibilities. Either use a combined key:
class MyKey {
int firstIndex;
int secondIndex;
// important: override hashCode() and equals()
}
Or a Map of Map:
Map<Integer, Map<Integer, Integer>> myMap;
Use a Pair as keys for the HashMap. JDK has no Pair, but you can either use a 3rd party libraray such as http://commons.apache.org/lang or write a Pair taype of your own.
Create a value class that will represent your compound key, such as:
class Index2D {
int first, second;
// overrides equals and hashCode properly here
}
taking care to override equals() and hashCode() correctly. If that seems like a lot of work, you might consider some ready made generic containers, such as Pair provided by apache commons among others.
There are also many similar questions here, with other ideas, such as using Guava's Table, although allows the keys to have different types, which might be overkill (in memory use and complexity) in your case since I understand your keys are both integers.
If they are two integers you can try a quick and dirty trick: Map<String, ?> using the key as i+"#"+j.
If the key i+"#"+j is the same as j+"#"+i try min(i,j)+"#"+max(i,j).
You can also use guava Table implementation for this.
Table represents a special map where two keys can be specified in combined fashion to refer to a single value. It is similar to creating a map of maps.
//create a table
Table<String, String, String> employeeTable = HashBasedTable.create();
//initialize the table with employee details
employeeTable.put("IBM", "101","Mahesh");
employeeTable.put("IBM", "102","Ramesh");
employeeTable.put("IBM", "103","Suresh");
employeeTable.put("Microsoft", "111","Sohan");
employeeTable.put("Microsoft", "112","Mohan");
employeeTable.put("Microsoft", "113","Rohan");
employeeTable.put("TCS", "121","Ram");
employeeTable.put("TCS", "122","Shyam");
employeeTable.put("TCS", "123","Sunil");
//get Map corresponding to IBM
Map<String,String> ibmEmployees = employeeTable.row("IBM");
You could create your key object something like this:
public class MapKey {
public Object key1;
public Object key2;
public Object getKey1() {
return key1;
}
public void setKey1(Object key1) {
this.key1 = key1;
}
public Object getKey2() {
return key2;
}
public void setKey2(Object key2) {
this.key2 = key2;
}
public boolean equals(Object keyObject){
if(keyObject==null)
return false;
if (keyObject.getClass()!= MapKey.class)
return false;
MapKey key = (MapKey)keyObject;
if(key.key1!=null && this.key1==null)
return false;
if(key.key2 !=null && this.key2==null)
return false;
if(this.key1==null && key.key1 !=null)
return false;
if(this.key2==null && key.key2 !=null)
return false;
if(this.key1==null && key.key1==null && this.key2 !=null && key.key2 !=null)
return this.key2.equals(key.key2);
if(this.key2==null && key.key2==null && this.key1 !=null && key.key1 !=null)
return this.key1.equals(key.key1);
return (this.key1.equals(key.key1) && this.key2.equals(key2));
}
public int hashCode(){
int key1HashCode=key1.hashCode();
int key2HashCode=key2.hashCode();
return key1HashCode >> 3 + key2HashCode << 5;
}
}
The advantage of this is: It will always make sure you are covering all the scenario's of Equals as well.
NOTE: Your key1 and key2 should be immutable. Only then will you be able to construct a stable key Object.
we can create a class to pass more than one key or value and the object of this class can be used as a parameter in map.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class key1 {
String b;
String a;
key1(String a,String b)
{
this.a=a;
this.b=b;
}
}
public class read2 {
private static final String FILENAME = "E:/studies/JAVA/ReadFile_Project/nn.txt";
public static void main(String[] args) {
BufferedReader br = null;
FileReader fr = null;
Map<key1,String> map=new HashMap<key1,String>();
try {
fr = new FileReader(FILENAME);
br = new BufferedReader(fr);
String sCurrentLine;
br = new BufferedReader(new FileReader(FILENAME));
while ((sCurrentLine = br.readLine()) != null) {
String[] s1 = sCurrentLine.split(",");
key1 k1 = new key1(s1[0],s1[2]);
map.put(k1,s1[2]);
}
for(Map.Entry<key1,String> m:map.entrySet()){
key1 key = m.getKey();
String s3 = m.getValue();
System.out.println(key.a+","+key.b+" : "+s3);
}
// }
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null)
br.close();
if (fr != null)
fr.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
You can download it from the below link:
https://github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java
https://github.com/VVS279/DoubleKeyHashMap
You can use double key: value hashmap,
DoubleKeyHashMap<Integer, Integer, String> doubleKeyHashMap1 = new
DoubleKeyHashMap<Integer, Integer, String>();
DoubleKeyHashMap<String, String, String> doubleKeyHashMap2 = new
DoubleKeyHashMap<String, String, String>();
Using org.apache.commons.lang3.tuple.Pair is very easy;
Map<String, Pair<String, Integer>> map= new HashMap<>();
map.put("key", Pair.of("a", 1));
int value = map.get("key").getRight();

HashMap in java cannot hash MyObject

I have defined a simple private class named SetOb which contains an int and a Set data structure. I have a HashMap in the 'main' method with SetOb as Key and Integer as value. Now as you can see in the main method, when I feed the HashMap with a SetOb instance and then look for an instance with exactly the same value, it returns 'null'. This has happened with me quite a few times before when I use my own defined data structures like SetOb as Key in HashMap. Can someone please point me what am I missing ?
Please note that in the constructor of SetOb class, I copy the Set passed as argument.
public class Solution {
public static Solution sample = new Solution();
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
}
public static void main(String[] args) {
Map<SetOb, Integer> m = new HashMap< SetOb, Integer>();
Set<Integer> a = new HashSet<Integer>();
for(int i =0; i<10; i++){
a.add(i);
}
SetOb x = sample.new SetOb(100, a);
SetOb y = sample.new SetOb(100, a);
m.put(x,500);
Integer val = m.get(y);
if(val!= null) System.out.println("Success: " + val);
else System.out.println("Failure");
}
}
Your x and y are not the same object instances hence contains is not able to match y against x, which ends up not finding the matching key/value in the Map.
If you want the match to succeed, please implement(override) hasCode & equals method in SetOb which will compare the field values.
Sample methods(Eclipse generated) as below:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + last;
result = prime * result + ((st == null) ? 0 : st.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SetOb other = (SetOb) obj;
if (last != other.last)
return false;
if (st == null) {
if (other.st != null)
return false;
} else if (!st.equals(other.st))
return false;
return true;
}
The default implementation of hashCode uses object identity to determine the hash code. You will need to implement hashCode (and equals) in your private class if you want value identity. For instance:
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
#Override
public boolean equals(Object other) {
if (other.class == SetOb.class) {
SetOb otherSetOb = (SetOb) other;
return otherSetOb.last == last && otherSetOb.st.equals(st);
}
return false;
}
#Override
public int hashCode() {
return 37 * last + st.hashCode();
}
}
SetOb needs to override the hashCode() and thus the equals() methods.
Hash-based collections use these methods to store (hashCode()) and retrieve (hashCode()) and equals()) your objects.

Categories

Resources