I would like to retrieve the original object of a key in a HashMap in Java, what is the best way to do it?
For example
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
Integer keyObj = new Integer(10);
Integer valueObj = new Integer(100);
// And add maybe 1 million other key value pairs here
//... later in the code, if I want to retrieve the valueObj, given the value of a key to be 10
Integer retrievedValueObj = map.get(10);
//is there a way to retrieve the original keyObj object with value 10 from map?
Basically, the user can query any value of key here just for the key object, 10 is just an example.
Some comment say, "you already have the x object, why do you want to get it?"
Well, this is the same as saying "you already have the value object, why do you want to get it?"
That is the purpose for the HashMap data structure, store and retrieval.
Retrieving a value object is easy but it seems no many people know how to retrieve the key object.
It seems like many people don't get why I want to achieve the object of 10 and ask why? why not just value 10. This is just a greatly simplified model.
Well, let me give a little bit context. The keyObj is data in another data structure and I need the exact reference of this original key object. Say, there is a linked list of all the key values, and if I want to remove a particular node in the linked list.
I am not only interested in the value "10", but also the memory location, i.e. the reference in Java of that "10" object. There could be many "10"'s in memory. But that exact object is what I want to retrieve.
The iterator approach answer below give an O(n) approach. But what I am looking for is an O(1) retrieval of the key OBJECT given the key value.
One way I can think of is to store the key object in value as well, like
class KeyAndValue {
public Integer key;
public Integer value;
public KeyAndValue(Integer key, Integer value) {
this.key = key;
this.value = value;
}
}
map<Integer, keyAndValueL> map = new map<Integer, keyAndValueL>();
Integer x = new Integer(10);
map.add(x, new KeyAndValue(x, 100));
//then I can retrieve the reference of x, given value of key 10
Integer newKeyObj = map.get(10).key;
but this approach uses more memory and looks like a hack to me. I am wondering if there is a more elegant way in Java.
A similar aproach but more generic is to store the "key + value" as an Entry instead of encapsule in another class.
Example:
Map<Integer, Entry<Integer, Integer>> map = new HashMap<Integer, Entry<Integer, Integer>>();
Integer x = new Integer(10);
map.put(x, new AbstractMap.SimpleEntry<Integer, Integer>(x, 100));
//then I can retrieve the reference of x, given value of key 10
Entry<Integer, Integer> keyObj = map.get(10);
try this
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
Integer keyObj = new Integer(10);
Integer valueObj = new Integer(100);
map.put(keyObj, valueObj);
Set<Integer> keys = map.keySet();
Iterator<Integer> iterator = keys.iterator();
while(iterator.hasNext()){
Integer x = iterator.next();
if(map.get(x) == 100)
System.out.println("key is "+ x);
}
You could store a key + value object "as the value" as you mention in your question.
What you are implementing is a variant of the flyweight pattern.
This is easiest implemented using a Map of every managed object to itself:
Map<T, T> cache = new HashMap<>();
And for every object you encounter:
T obj; // comes from somewhere
obj = cache.computeIfAbsent(obj, v -> obj); // reuse, or add to cache if not found
This has O(1) time complexity and uses only one extra object reference for each object so managed.
Related
I was wondering how could i link my XSSFSheet by a string? I need a variable similar to Map, but it should return XSSFSheet, not object.
Something like this:
List<XSSFSheet, String> list = new ArrayList<XSSFSheet, String>
list.add(mySheet1, "ID154");
list.add(mySheet2, "ID4564");
list.get("ID4564"); //Gets mySheet1 by that ID
The easiest way to do so is a Map, but it would be a (very common) misuse of this datastructure. The correct way would be to create a simple dataholder class containing your two strings and have a List of objects of these datatypes.
On the other hand, reading your question again... a Map might not be so wrong at all in this case asy you have key-value-pairs.
So Map<String, XSSFSheet> map = new HashMap<>() should be what you're looking for.
No it is not. A list is an object for storing a collection of objects that are of the same type.to store key value pairs like what you want to do you'd need to use a HashMap object which is designed to store key, value pairs.
HashMaps come in the form HashMap<key, value> with key being the type for the key, and value being the type for the corresponding value.
Your code would then look like this:
HashMap<String, XSSFSheet> list = new HashMap<String, XSSFSheet>();
list.put("ID154", mySheet1);
list.put("ID4564", mySheet2);
list.get("ID4564"); //Gets mySheet1 by that ID
For more information read this page.
I don't think there is any structural overhead in using Map in your case.
Complexity of accessing an entry in a HashMap is O(m), where m is the number of elements in the longest linked list associated with an entry in a HashMap.
But if you are very much specific in not using Map, then you can make use of the KeyValue Tuple from the JavaTuples library.
A KeyValue is a Tuple from JavaTuples library that deals with only 2 elements – a key and a value.
Since this KeyValue is a generic class, it can hold any type of value in it.
Since KeyValue is a Tuple, hence it also has all the characterstics of JavaTuples:
They are Typesafe
They are Immutable
They are Iterable
They are Serializable
They are Comparable (implements Comparable)
They implement equals() and hashCode()
They also implement toString()
Class Declaration
public final class KeyValue<A, B> extends Tuple
implements IValueKey<A>, IValueValue<B>
Class hierarchy
Object ↳ org.javatuples.Tuple
↳ org.javatuples.KeyValue
So in your case you can use something like this.
Change according to your use case.
package com.test.UnitTests;
import java.util.*;
import org.javatuples.KeyValue;
class KeyValueTest {
public static void main(String[] args) {
KeyValue<Integer, String> kv1 = KeyValue.with(Integer.valueOf(1), "Maths");
KeyValue<Integer, String> kv2 = KeyValue.with(Integer.valueOf(2), "English");
List<KeyValue> testKVs = new ArrayList<KeyValue>();
testKVs.add(kv1);
testKVs.add(kv2);
//assuming that you want to fetch the value for key = 2
int key = 2;
String value = fetchValue(key,testKVs);
if(value!=null) {
System.out.println("Value for Key: "+key +" = "+value);
}else {
System.out.println("No Key-Value pair found with Key:"+key);
}
}
private static String fetchValue(int i, List<KeyValue> testKVs) {
for (KeyValue testKv : testKVs) {
if ((int) testKv.getKey() == i) {
return (String)testKv.getValue();
}
}
return null;
}
}
To learn about javatuples more, use this link:
https://www.geeksforgeeks.org/keyvalue-class-in-java-tuples/
You can download the jar at:
http://www.java2s.com/Code/Jar/j/Downloadjavatuples10jar.htm
Hi i have bytearrays of size 8 bytes each and i need to store them in a hashmap or hashtable. For example say i have 1000 blocks... then it will store 1st key and value(bytearray) and then when block2 is passed it should check if it is already present in hahtable and if not there it should increment and count should be incremented. I have written code to store but the problem is that it is not able to search, may be bacuse of the way i'm storing in byte array.
Code:
int collisions = 0;
Hashtable<Integer, Long> ht = new Hashtable<Integer, Long>();
// Given bloc1 value here
if(ht.contains(bloc1)) {
collisions++;
}
else {
ht.put(i,ByteBuffer.wrap(bloc1).getLong());
}
Problem is: ht.contains is not giving the desired o/p
byte[] objects, and other array objects in Java has equals() method inherited from Object, i. e. comparing by reference, not by array contents.
The simpliest way to resolve your problem (without additional dependencies to libraries) is to store 8-byte arrays as longs:
Map<Long, Value> map = new HashMap<>();
...
// map.put(byteArray, value);
map.put(ByteBuffer.wrap(byteArray).getLong(), value);
Solution:
int collisions = 0;
Hashtable ht = new Hashtable();
// Given bloc1 value here
if(ht.contains(ByteBuffer.wrap(bloc1).getLong()))
{collisions++;}
else {
ht.put(i,ByteBuffer.wrap(bloc1).getLong());
}
public class random {
public static class A{
int id= 1;
public int hashCode(){
return id;
}
}
public static void main(String[] args) {
Map<Integer, Integer> map= new HashMap<Integer, Integer>();
A a = new A();
A b = new A();
map.put(a.id, 1);
map.put(b.id, 2);
System.out.println(map.get(a.id));
System.out.println(map.get(b.id));
System.out.println(map.size());
}
}
output is
2
2
1
a and b have the same hashcode(bucket) and inside this bucket is (1 and 2). It says only one node exists which is b with the value of 2. When 2 hashcodes are the same do they override the current value? I read it is not the case.
from tutorial
*"Since hashcode is same, bucket location would be same and collision will occur in HashMap, Since HashMap use LinkedList to store object, this entry (object of Map.Entry comprise key and value ) will be stored in LinkedList.
Read more: http://javarevisited.blogspot.com/2011/02/how-hashmap-works-in-java.html#ixzz2sCM7fNED*
does this mean that my map now has 1 bucket with 2 values? how do I grab the value of (1)
You are using the Integer id as a key in the Map, not a or b. So the hashcode() of A is not used at all.
To get the values from a map, use the get method.
If you wanted to use a1 or a2 as the key, you would need to declare the map as such:
Map<A, Integer> map = new HashMap<>();
from the doc of hashmap of java (http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html)
HashMap.put()
Associates the specified value with the specified key in this map. If
the map previously contained a mapping for the key, the old value is
replaced.
So the new value will replace the old value.
Both of them have the same id of "1", so when you add them to the map under the key of their id, they both get added with the key 1. So the second one overrides the first.
You've made a bit of a mess.
A Map maps between keys and values. In your example, you're mapping integers to integers. First you're trying to map 1 to 1, then 1 to 2. A map can only map a key once - if you map the key a second time, it overwrites the first mapping.
Hash collisions happen when two different keys have the same hash value. If you created a Map<A, Integer> and mapped A and B to to some values, you would have had a collision, but would have gotten a map with two elements.
I need to create a List that records two columns {int, String}. I think ArrayList is what I need but I cant get my head around it. I pulled the Strings from a database and the int is the index value which I need to identify the strings position for later.
List<List<String>> strArray = ArrayList<List<String>>;
then could I do something like strArray.add().add() for each row I pull from the database?
I think you should use a HashMap with int as key and String as value if your int values are going to be unique.
Map<Integer,String> myMap = new HashMap<Integer,String>();
myMap.put(1,"ABC");
Note that as Map is a collections and java collections do not store primitive like int, they store objects so you have to use Integer wrapper class for your int values.
Refer this link Why can Java Collections not directly store Primitives types?
Another approach would be to make a custom object:
Class CustomObject {
int value1;
String value2;
CustomObject(int v1, String v2) {
value1 = v1;
value2 = v2;
}
}
And then use it:
List<CustomObject> myList = new ArrayList<CustomObject>();
CustomObject o1 = new CustomObject(1, "one");
myList.add(o1);
// etc.
If the int values are unique and you want to consider them keys, then a Map would work as others have suggested.
If you need just two values you can use native Pair class
List<Pair> mPairs = new ArrayList<Pair>();
Pair pair = new Pair(123,"your string");
mPairs.add(pair);
This will be a good decision if you int values are not unique and so you can not use HashMap
If your IDs are not unique, you still can use Map :
Map<Integer, String> map = new IdentityHashMap<Integer, String>();
map.put(new Integer(1), "string");
IdentityHashMap - use native hashCode implemetation for each OBJECT, so you don't need unique IDs, but you MUST create ALL Integers via operator 'new', and don't use autoboxing, because there is some cache mechanism.
Also there is JVM parameter, which controlls cache size '-XX:AutoBoxCacheMax='.
But using this parameter you can't disable cache, if you set size to the zero, then cache will ignore it and use default: [-128; 127].
This parameter is only for Integers, there is no such kind of parameter for Long.
UPDATE
Also for non unique keys you could use some sort of multimap:
Map> map
And store in it your values with nonunique keys:
map.put(1, new ArrayList<String>());
map.get(1).add("value1");
map.get(1).add("value2");
You can use HashMap for that for example.
Also you can find MultiMap implementation in google-collections: 'guava'.
I think you may wrap the int and string in a class, then put the class objects in List.
Map is an object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
I think it would be better if you use Map<Integer,String> where key(Integer) would be the index which will pointing to String value.
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"str1");
map.put(2,"str2");
...
So what I have been trying to do is use a TreeMap I previously had and apply it to this method in which I convert it into a set and have it go through a Map Entry Loop. What I wish to do is invert my previous TreeMap into the opposite (flipped) TreeMap
'When I run my code, it gives me a comparable error. Does this mean I have to implement the comparable method? I convereted the arrayList into an Integer so I thought the comparable method would support it. Or is it just something wrong with my code
Error: Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Comparable
Overview: Originally, my intended purpose for the program was to make a Treemap that read from a text document and specifically found all the words and the index/rows of where the words were located. Now I wish to make a "top ten" list that contains the most used words. I wanted to "flip" my treemap so that the integer values would be what would be put in order and the string would follow
public static void getTopTenWords(TreeMap<String, ArrayList<Integer>> map) {
Set<Map.Entry<String, ArrayList<Integer>>> set = map.entrySet();
TreeMap<Integer, String> temp = new TreeMap<Integer, String>();
int count = 1;
for(Map.Entry<String, ArrayList<Integer>> entry : set){
if(temp.containsKey(entry.getValue())) {
Integer val = entry.getValue().get(count);
val++;
temp.put(val, entry.getKey());
}
else {
temp.put(entry.getValue().get(count), entry.getKey());
}
count++;
}
}
Now I wish to make a "top ten" list that contains the most used words.
I wanted to "flip" my treemap so that the integer values would be what
would be put in order and the string would follow
Note that a Map contains only unique keys. So, if you try to keep your count as key, then you would need to put it in your Map by creating a new object with new Integer(count).
If you put your count in Map like: - map.put(2, "someword"), then there are chances that your previous count value gets overwritten, because Integer caches the values in range: - [-128 to 127]. So, the integer values between these range will be interned if you don't create a new object. And hence two Integer with value say 2 will point to same Integer object, and hence resulting in duplicate key.
Secondly, in your code: -
if (temp.containsKey(entry.getValue()))
using the above if statement, you are comparing an ArrayList with an Integer value. temp contains key which are integers. And values in entry are ArrayList. So, that will fail at runtime. Also, since your orginal Map contains just the location of the word found in the text file. So, just what you need to do is, get the size of arraylist for each word, and make that a key.
You would need to modify your code a little bit.
public static void getTopTenWords(TreeMap<String, ArrayList<Integer>> map) {
Set<Map.Entry<String, ArrayList<Integer>>> set = map.entrySet();
TreeMap<Integer, String> temp = new TreeMap<Integer, String>();
for(Map.Entry<String, ArrayList<Integer>> entry : set) {
int size = entry.getValue().size();
int word = entry.getKey();
temp.put(new Integer(size), word));
}
}
So, you can see that, I just used the size of the values in your entry set. And put it as a key in your TreeMap. Also using new Integer(size) is very important. It ensures that every integer reference points to a new object. Thus no duplication.
Also, note that, your TreeMap sorts your Integer value in ascending order. Your most frequent words would be somewhere at the end.