Related
I was expecting the WeakReference class to override hashCode and equals methods like this
class WeakReference<T>{
T ref;
int hashCode(){
return ref.hashCode();
}
boolean equals(Object o){
return ref.equals(o);
}
}
So that I could use We WeakReference directly as key in hashmaps like
Person p1 = new Person("p1");
WeakReference<Person> wr = new WeakReference<Person>(p1);
map.put(wr, "some value object");
But when I tested I found out that hashCode and equals are not overridden
Person p1 = new Person("p1");
WeakReference<Person> wr = new WeakReference<Person>(p1);
WeakReference<Person> wr2 = new WeakReference<Person>(p1);
System.out.println(wr.hashCode()); // prints x
System.out.println(wr2.hashCode()); // prints y
System.out.println(wr.equals(wr2)); // prints false
Any specific reasons reasons that hashCode and equals are not overridden in WeakReference class?
An important aspect of any key on a Map (or element of a Set) is that it must be immutable (or at least not change) once it has been added to the collection. Changing a key has undefined behaviour which is highly unlikely to work.
A WeakReference can change at any time due to a GC being performed i.e. in ways you have no control over, which makes the equals/hashCode inappropriate for general collections which use these.
I was trying to make MyWeakConcurrentHashMap
A simple way of doing this is to have an array of WeakHashMaps. e.g. 32 partitions . Use the hashCode() to determine which WeakHashMap to use. This way you can have a thread accessing each of the individual WeakHashMap at once (best case)
As you have more concurrency you can increase the number of partitions.
It seems like WeakHashMap uses Entry which extends WeakReference
and overrides both hashCode and equals, you can take a look of their implementation.
/**
* The entries in this hash table extend WeakReference, using its main ref
* field as the key.
*/
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
#SuppressWarnings("unchecked")
public K getKey() {
return (K) WeakHashMap.unmaskNull(get());
}
public V getValue() {
return value;
}
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
K k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
V v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public int hashCode() {
K k = getKey();
V v = getValue();
return Objects.hashCode(k) ^ Objects.hashCode(v);
}
public String toString() {
return getKey() + "=" + getValue();
}
}
I have following code:
public class SimpleHashMap<K, V> extends AbstractMap<K, V>{
static final int SIZE = 997;
#SuppressWarnings("unchecked")
LinkedList<MapEntry<K, V>>[] buckets =
new LinkedList[SIZE];
public V put(K key, V value)
{
V oldValue = null;
int index = Math.abs(key.hashCode()) % SIZE;
if(buckets[index] == null)
buckets[index] = new LinkedList<MapEntry<K, V>>();
LinkedList<MapEntry<K, V>> bucket = buckets[index];
MapEntry<K, V> pair = new MapEntry<K, V>(key, value);
boolean found = false;
ListIterator<MapEntry<K, V>> iter = bucket.listIterator();
int probes = 0;
while(iter.hasNext())
{
MapEntry<K, V> iPair = iter.next();
probes++;
if(iPair.getKey().equals(key)){
oldValue = iPair.getValue();
iter.set(pair); //zastapienie starego nowym
found = true;
System.out.println("Colision at: " + iPair + " : " + probes + (probes == 1 ? " probe " : " probes ")
+ "needed");
break;
}
}
if(!found)
buckets[index].add(pair);
return oldValue;
}
public V remove(Object o)
{
V removed = null;
if(this.get(o) != null)
{
int index = Math.abs(o.hashCode()) % SIZE;
for(MapEntry<K, V> pair : buckets[index])
{
if(pair.getKey().equals(o))
{
removed = pair.getValue();
buckets[index].remove(buckets[index].indexOf(pair));
break;
}
}
}
return removed;
}
#Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K, V>>();
for(LinkedList<MapEntry<K, V>> bucket : buckets){
if(bucket == null) continue;
for(MapEntry<K, V> mpair : bucket)
set.add(mpair);
}
return set;
}
}
I'm interestring in it's method:
public V remove(Object o)
{
V removed = null;
if(this.get(o) != null)
{
int index = Math.abs(o.hashCode()) % SIZE;
for(MapEntry<K, V> pair : buckets[index])
{
if(pair.getKey().equals(o))
{
removed = pair.getValue();
buckets[index].remove(buckets[index].indexOf(pair));
break;
}
}
}
return removed;
}
When we create instance of above class parametrized in this way:
SimpleHashMap<String, String> m =
new SimpleHashMap<String, String>();
add some elements by putAll method...;
Why I can't do:
if(this.get(o).equals(o))
instead of:
if(this.get(o) != null)
I know of errasing types on compile level, but if above .equals means that we compare two Object types, so we compare address, not value. OK, it is logical.
But what we compare here:
if(pair.getKey().equals(o))
It looks like we comparing two String types. Now I'm confused, could someone explain to me how exactly this works in this cases? Sorry for my english. I hope someone help me with this. Thanks a lot.
The parameter for remove(Object o) is a key, so if(pair.getKey().equals(o)) compares the pair's key to the key parameter (compare apples to apples), but this.get(o) returns a value, so if(this.get(o).equals(o)) is comparing a value to a key, which is meaningless (compare apples to oranges).
When I try to access a generic method from my main class it gives me an error.
When I do the following:
Integer key = map.remove(15);
then Eclipse says "The method remove(String) in the type IMiniMap<String,Integer> is not applicable for the arguments (int)" What's the way to access a method of generic type?
public class Tester {
public static void main(String[] args)
{
IMiniMap<String,Integer> map = new SimpleListMM<String,Integer>();
map.put("B",15);
map.put("A",5);
map.put("R",-5);
map.put("D",55);
map.put("Poems",128);
map.put("Plays",256);
// System.out.println(map.size());
// map.put("B", 22);
// System.out.println(map.keys());
//System.out.println(map.toString());
Integer key = map.remove(15);
}
}
public abstract class AbstractListMM<K,V> implements IMiniMap<K,V>{
protected List <K> keys;
protected List <V> vals;
// Initialize the lists of keys and values with a concrete instance
public AbstractListMM()
{
this.keys = new ArrayList<K>();
this.vals = new ArrayList<V>();
}
public AbstractListMM(List <K> keys, List <V> vals)
{
this.keys = keys;
this.vals = vals;
}
// Return the number of bindings based on the size of the key list
public int size()
{
return keys.size();
}
// Based on the lists size
public boolean isEmpty()
{
return (keys.isEmpty() && vals.isEmpty());
}
// Make a (shallow) copy of the keys list and return it
public List<K> keys()
{
List<K> newKeys = this.keys;
return newKeys;
}
// Make a (shallow) copy of the vals list and return it
public List<V> values()
{
List<V> vals = this.vals;
return vals;
}
// Use this.indexOf() to locate the given key as quickly as possible
public boolean contains(K key)
{
int pos = this.indexOf(key);
if(pos < 0)
return false;
else
return true;
/*if(this.indexOf(key) < 0)
return false;
else
return true;*/
}
// Use this.indexOf() to determine if a given key is present and
// return its associated value; return null if the key is not
// present
//
// TARGET COMPLEXITY: Same speed as indexOf()
public V get(K key)
{
int pos = this.indexOf(key);
if(pos < 0)
return null;
else
return vals.get(pos);
}
// Use this.indexOf() to determine the location of the given
// key/value and remove it from the corresponding lists
//
// TARGET COMPLEXITY: O(N) due to list elements shifting
public V remove(K key)
{
int pos = this.indexOf(key);
if(pos < 0)
return null;
else
return vals.remove(pos);
}
// Find the numeric index of the key as quickly as possible based on
// the type of ListMM being implemented. Return a negative number if
// the given key is not present.
public abstract int indexOf(K key);
// Associate the given key with the given value in the
// lists. Creates an ordering of keys that is compatible with how
// indexOf() works to locate keys.
public abstract V put(K key, V value);
}
You already specify that K type
AbstractListMM<K,V> implements IMiniMap<K,V>
will be a type String which is from here:
IMiniMap<String,Integer> map = new SimpleListMM<String,Integer>();
And therefore you need to supply your remove method remove(K key) as a String not an Integer
map.remove("B");
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();
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();