Choosing the perfect data structure for the below data in java - java

I have to choose one data structure for my need below i am explaining the conditions there are following values
abc,def,rty,ytr,dft which all are map to row R1B1 (actully key is combination of R1+B1)
abEERc,dFFFef,rGGty which all are map to row R1B2 (actully key is combination of R1+B2)
KEY VALUE
abc,def,rty,ytr,dft ---> R1B1
abEERc,dFFFef,rGGty ---> R1B2
now, for example, let's say, if i get ytr then i would be able to retrieve R1B1
or, let's say, i get the value rGGty then i would be able to retrieve R1B2
now the case is that matters is of search, complexity and the time taken as the things have to go in sequence
for example, it will first pick the first line to search ytr, it will first match it with abc which will not match then will have to match with def it will not again match then it will match with rty which will not also match then it will finally match with ytr and finally it will find the key R1B1 finally
similarly if the second string need to be searched lets say rGGty then it would scan first row in which it will not find the value then search would continue to second row and also in second row in the third element it would get rGGty as element then it would retrieve R1B2 as value
let's say, if put this thing in map then a sequence search will go on key and then only we will be able to find the corresponding value
Folks please advise which will be the best data structure i can implement in java in which i will have to search the keys items to find the corresponding value in very fast time also which will not hit the performance too
,the kind of data structure performance should be very high
Please advise folks

Key-value pairs can be accessed in O(1) using a HashMap. However if you use HashMap<String, String> then updating the value will be painful because Strings are immutable. This means you will have to check all entry sets and if the value matches update it. So you could create a helper class for the value and let all keys point to an instance of this class. Here is a stub with the most important functions, I guess you can add the rest yourself.
public class MyDataStructure {
private Map<String, MyValue> key_value = new HashMap<String, MyValue>();
private Map<String, MyValue> value_MyValue = new HashMap<String, MyValue>();
public void set(String key, String value) {
MyValue v = value_MyValue.get(value);
if (v == null) { // should rarely happen, could check with containsKey
v = new MyValue(value);
value_MyValue.put(v);
}
key_value.put(key, v);
}
public String get(String key) {
return key_value.get(key).getValue(); // key might not exist
}
public String changeValue(String oldValue, String newValue) {
MyValue v = value_MyValue.remove(oldValue); // oldValue might not exist
v.setValue(newValue);
value_MyValue.put(newValue, v);
// will not work if newValue already exists... then you will have to merge
}
private class MyValue() {
private String value;
public MyValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}

I think its best to keep it simple until poor performance shows a need for some kind of improvement. Even if many map values are the same string, that should be ok since Java stores only one copy in heap. If the sets of keys mapped to a single string gets very large some performance improvement may be possible by doing two lookups -- first to determine set membership and second to retrieve the value of the key associated with the set. That would be easy to implement. For now here is an immediately straightforward approach:
import java.util.*;
public class HashMapDemo {
static HashMap<String, String> map = new HashMap<String, String>();
public static void lookup(String key, String value) {
if (map.get(key) == value) {
System.out.println(key + " lookup ok");
} else {
System.out.println(key + " lookup produced" + map.get(key));
}
}
public static void main(String[] args) {
// requirements:
// abc,def,rty,ytr,dft ---> R1B1
// abEERc,dFFFef,rGGty ---> abEERc
Set<String> kset1 = new HashSet<String>(Arrays.asList("abc", "def",
"rty", "ytr", "dft"));
Set<String> kset2 = new HashSet<String>(Arrays.asList("abEERc",
"dFFFef", "rGGty"));
for (String s : kset1) {
map.put(s, "R1B1");
}
for (String s : kset2) {
map.put(s, "abEERc");
}
// testing value lookup with key
for (String s : kset1) {
lookup(s, "R1B1");
}
// prints:
// abc lookup ok
// dft lookup ok
// def lookup ok
// rty lookup ok
// ytr lookup ok
for (String s : kset2) {
lookup(s, "abEERc");
}
// prints:
// rGGty lookup ok
// abEERc lookup ok
// dFFFef lookup ok
// change key "R1B1" to "XYZ"
for (String s : kset1) {
map.put(s, "XYZ");
}
// test the change
for (String s : kset1) {
lookup(s, "XYZ");
}
// prints:
// abc lookup ok
// dft lookup ok
// def lookup ok
// rty lookup ok
// ytr lookup ok
}
}

Related

Check if a sentence contains a word from a list of word

I want to check if a sentence contains a word from a list of words mapped to a category. So i have a class KeyValue.java with words, category names and a method filterCategory to check if it contains the word. Now i have a 10,000 keywords mapped different categories for the text. But the trouble is it is way to slow. Can you suggest some alternate methods to speed up the classification. Thanks for the help.
public class KeyValue {
private String key;
private String value;
public KeyValue(String key, String value) {
this.key = key;
this.value= value;
}
public KeyValue() {
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
Classification.java
class Classification
{
private static List<KeyValue> keyMap = new ArrayList<KeyValue>();
static{
getWordMap();
}
public static List<KeyValue> getWordMap()
{
if(keyMap.size()==0)
{
keyMap.add(new KeyValue("sports","football"));
keyMap.add(new KeyValue("sports","basketball"));
keyMap.add(new KeyValue("sports","olympics"));
keyMap.add(new KeyValue("sports","cricket"));
keyMap.add(new KeyValue("sports","t20"));
}
}
public static KeyValue filterCategory(String filteredText)
{
KeyValue kv = null;
for(KeyValue tkv:keyMap)
{
String value = tkv.getValue();
String lc = filteredText.toLowerCase();
lc = FormatUtil.replaceEnglishSymbolsWithSpace(lc);//remove symbols with space and then normalizes it
String lastWord="";
if(lc.contains(" "))
{
lastWord = lc.substring(lc.lastIndexOf(" ")+1);
if(lc.startsWith(value+" ") || lc.contains(" "+value+" ") || value.equals(lastWord))
{
kv = new KeyValue(tkv.getKey(), tkv.getValue());
break;
}
}
else if(lc.contains(value))
{
kv = new KeyValue(tkv.getKey(), tkv.getValue());
break;
}
}
if(kv==null)
{
return new KeyValue("general","0");
}
else
{
kv.setValue("100");
return kv;
}
}
}
Your implementation is sound but uses an Exhaustive or Brute-Force Search algorithm with your KeyValue object instead of a faster matching algorithm such as Hashing with a HashMap or Hashtable object.
Assumptions
You have 10,000 mapped words.
You are attempting to match those words against an English sentence or phrase such as "The quick brown fox jumps over the lazy dog"
The Problem
Your logic, as it is written, will perform a brute-force search attempting a possible 10,000 matches for each word in your sentence. Using the phrase given above will create (10,000) x (9) = 90,000 maximum attempts if each word in the sentence does not exist within your KeyValue object.
This logic creates a worst-case, or Big-O, performance hit of Θ(n) where n represents the number of words in your list. This is called a linear search. A lazy improvement to this method would be to use a sorted list, granting you a better Θ(log(n)) logarithmic search time.
The Fix
Instead of performing your brute-force search, use a hashing algorithm that will perform lookups on whole words at a time; or, if you want to perform pattern matching with character shifting, look at the Rabin—Karp Hash Algorithm. In the simplified case of just matching whole words, your algorithm will break down your sentence's words into tokens (like you have now), and then use a hash function lookup against your hashmap of values and associated categories.
Your new logic will carry a Big-O performance of Θ(1). This constant-time matching will greatly improve the speed of your application.
Pseudocode
// Adapting your KeyValue into a simple <Value, Key> map e.g. <"football", "sports">
//HashMap<String, String> map = new HashMap<String, String>();
// Adapting your KeyValue into a <Value, Set<Key>> map for multiple
// category keys e.g. <"football", <"sports","sunday","games">>
HashMap<String, Set<String>> map = new HashMap<String, Set<String>>();
// build the hashmap with your values and categories
Set<String> categories = new HashSet<String>();
categories.add("sports");
categories.add("sunday");
categories.add("games");
map.put("football", categories);
...
// sanitize your input
String lc = filteredText.toLowerCase();
lc = FormatUtil.replaceEnglishSymbolsWithSpace(lc);
// tokenize your sentence
String[] tokens = lc.split("\\s");
...
// search tokens against your hashmap
for (String token : tokens) {
// search the token against the hashmap
if (map.containsKey(token)){
Set<String> cats = map.get(token);
...
} else {
...
}
}
I don't understand why you do not use Java's util.Map for this concern, but i advise you to iterate use :
lc = FormatUtil.replaceEnglishSymbolsWithSpace(lc);
String result= Arrays.stream(lc.split(" ")).filter(s -> s.equals(value)).findFirst().orElse("");
if(result.length()>0) {
kv = tkv;
}
Based on the suggestions i am posting the fastest code i could come up with.
KeyValue based List has been modified into simple HashMap
private static HashMap<String,String> map = new HashMap<String,String>();
Thanks for the suggestions. It's now scalable to be put into production.
public static KeyValue filterCategory(String filteredText)
{
KeyValue kv = null;
filteredText = filteredText.toLowerCase();
filteredText = FormatUtil.replaceEnglishSymbolsWithSpace(filteredText);
StringTokenizer tokenizer = new StringTokenizer(filteredText);
while(tokenizer.hasMoreTokens()) {
String temp = tokenizer.nextToken();
if(map.containsKey(temp))
{
kv = new KeyValue(map.get(temp),"100");
break;
}
}
if(kv==null)
{
kv= new KeyValue("general","0");
}
return kv;
}

Add number in map java

I am creating a function that loops through a string, separates it by comma and then takes the key from the second item in the array and the value from the 1st after splitting the string.
I then want to place these values in a map. This works perfectly, however if i have two strings with the same key it doesn't add the value up it just replaces it.
For example if my string was
123,totti 100,roma, 100,totti
I would want
totti 223
roma 100
Here is my code
private void processCallLogs(String[] splitCalls) {
for (String individualCall : splitCalls) {
int duration = 0;
String[] singleCall = individualCall.split(",");
duration += DurationParser.returnDuration(singleCall[0]);
this.cost += CalculateCost.calculateCostPerCall(singleDuration);
if (totalCallDurations.containsKey(singleCall[1])) {
totalCallDurations.put(singleCall[1], singleDuration);
} else {
totalCallDurations.put(singleCall[1], duration);
}
}
}
You can replace the if with something like this:
if (totalCallDurations.containsKey(singleCall[1])) {
duration += totalCallDurations.get(singleCall[1]);
}
totalCallDurations.put(singleCall[1], duration);
Create a map and update the value if the key is present
public static void main(String[] args) {
myMap = new HashMap<>();
// 123,totti 100,roma, 100,totti
addToMap("totti", 123);
addToMap("roma", 100);
addToMap("totti", 100);
System.out.println(myMap);
}
private static void addToMap(String string, int i) {
int t = i;
if (myMap.get(string) != null) {
t += myMap.get(string);
}
myMap.put(string, t);
}
If you're using Java 8, you can do this easily with the Map.merge() method:
totalCallDurations.merge(singleCall[1], duration, Integer::sum);
If you want to make a map that will add the values together instead of replacing, I would recommend extending the Map type to make your own map. Since Map is very abstract. I would extend HashMap. (I suggest this both for code style and because it will make your code more extendable).
public class AdderMap extends HashMap<String, Integer> { // This extends the HashMap class
public Integer get(String key) { // This overrides the Map::get method
if(super.containsKey(key)) return super.get(key); // If the key-value pairing exists, return the value
else return 0; // If it doesn't exist, return 0
}
public Integer put(String key, Integer value) { // This overrides the Map::put method
Integer old_value = this.get(key); // Get the former value of the key-value pairing (which is 0 if it doesn't exist)
super.put(key, old_value + value); // Add the new value to the former value and replace the key-value pairing (this behaves normally when the former value didn't exist)
return old_value; // As per the documentation, Map::put will return the old value of the key-value pairing
}
}
Now, when you initialize your map, make it an AdderMap. Then, you can just use put(String, Integer) and it will add it together.
The advantage of this solution is that it helps with keeping your code clean and it allows you to use this type of map again in the future without needing separate code in your main code. The disadvantage is that it requires another class, and having too many classes can become cluttered.

Contains operation in hashmap key

My hashmap contains one of entry as **key: its-site-of-origin-from-another-site##NOUN** and **value: its##ADJ site-of-origin-from-another-site##NOUN**
i want to get the value of this key on the basis of only key part of `"its-site-of-origin-from-another-site"``
If hashmap contains key like 'its-site-of-origin-from-another-site' then it should be first pick 'its' and then 'site-of-origin-from-another-sit' only not the part after '##'
No. It would be a String so it will pick up whatever after "##" as well. If you need value based on substring then you would have to iterate over the map like:
String value = map.get("its...");
if (value != null) {
//exact match for value
//use it
} else {//or use map or map which will reduce your search time but increase complexity
for (Map.Entry<String, String> entry : map.entrySet()) {
if (entry.getKey().startsWith("its...")) {
//that's the value i needed.
}
}
}
You can consider using a Patricia trie. It's a data structure like a TreeMap where the key is a String and any type of value. It's kind of optimal for storage because common string prefix between keys are shared, but the property which is interesting for your use case is that you can search for specific prefix and get a sorted view of the map entries.
Following is an example with Apache Common implementation.
import org.apache.commons.collections4.trie.PatriciaTrie;
public class TrieStuff {
public static void main(String[] args) {
// Build a Trie with String values (keys are always strings...)
PatriciaTrie<String> pat = new PatriciaTrie<>();
// put some key/value stuff with common prefixes
Random rnd = new Random();
String[] prefix = {"foo", "bar", "foobar", "fiz", "buz", "fizbuz"};
for (int i = 0; i < 100; i++) {
int r = rnd.nextInt(6);
String key = String.format("%s-%03d##whatever", prefix[r], i);
String value = String.format("%s##ADJ %03d##whatever", prefix[r], i);
pat.put(key, value);
}
// Search for all entries whose keys start with "fiz"
SortedMap<String, String> fiz = pat.prefixMap("fiz");
fiz.entrySet().stream().forEach(e -> System.out.println(e));
}
}
Prints all keys that start with "fiz" and sorted.
fiz-000##whatever
fiz-002##whatever
fiz-012##whatever
fiz-024##whatever
fiz-027##whatever
fiz-033##whatever
fiz-036##whatever
fiz-037##whatever
fiz-041##whatever
fiz-045##whatever
fiz-046##whatever
fiz-047##whatever
fizbuz-008##whatever
fizbuz-011##whatever
fizbuz-016##whatever
fizbuz-021##whatever
fizbuz-034##whatever
fizbuz-038##whatever

Associate multiple values with the single key and also maintain the order of values

I have a class which contain the following members
private String patientName;
private String patientPhoneNumber;
now I have multiple names attached with the phone No. for example
1234567, AAA
1234567, BBB
8765432, CCC
8765432, GGG
Now I want to store them in a Map but the phone No. should be the key having multiple values, for 1234567 i should have value AAA and BBB, please advise how can I store the multiple values with the single key in map here my key is Phone No. and then please let me know if I want to print in console then ow would I iterate over the Map
Also please not that I want to maintain the order also let say first I get the value AAA and then BBB so i should maintain these order also, since I get this is just a example but in my scenario I will be getting this value from backend so to maintain the order is also necessary please advise.
You may try something like this:
HashMap<String,LinkedList<String>> map
private Map<String,List<String>> patients;
public void setPatientNumber(String number, String patient){
List<String> list = patients.get(number);
if(list == null){
list = new ArrayList<String>();
patients.put(number,list);
}
list.add(patient);
}
new Map<String, TreeSet<String>>()
Will allow you to store the values in a TreeSet (sorted...).
To print them:
for(Map.Entry entry : phoneBook.entries()){
System.out.println(entry.key() + ":");
TreeSet names = entry.value();
for(String name : names){
System.out.println("\t" + name);
}
}
You can add, like this, if you want case insensitive ordering:
TreeSet<String> nameSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
phoneBook.add(number, nameSet);
Use a LinkedHashMap and an ArrayList for each values :
LinkedHashMap<String,ArrayList<String>> phoneNumbers = new LinkedHashMap<String,ArrayList<String>>();
// register new phone number
phoneNumbers.put("1234567", new ArrayList<String>());
// add names to the phone number
phoneNumbers.get("1234567").add("AAA");
phoneNumbers.get("1234567").add("BBB");
Both collections preserve the insertion ordering.
** Edit **
Here, this is roughly what you'd need (this was done on the spot without much testing, but you should get the idea). Since your ordering may vary, I thought limiting duplicates and providing a comparator for ordering should be preferable (as suggested by other answers) :
public class LinkedMultiMap<K,V> {
private Comparator<V> comparator;
private LinkedHashMap<K,Set<V>> entries;
public LinkedMultiMap() {
this(null);
}
public LinkedMultiMap(Comparator<V> comparator) {
this.comparator = comparator;
this.entries = new LinkedHashMap<K, Set<V>>();
}
public boolean add(K key, V value) {
if (!entries.containsKey(key)) {
entries.put(key, new TreeSet<V>(comparator));
}
return entries.get(key).add(value);
}
public Collection<V> get(K key) {
return entries.get(key);
}
public boolean remove(K key, V value) {
boolean removed = false;
if (entries.containsKey(key)) {
removed = entries.get(key).remove(value);
if (entries.get(key).isEmpty()) {
entries.remove(key);
}
}
return removed;
}
public Collection<V> removeAll(K key) {
return entries.remove(key);
}
public Iterator<K> keyIterator() {
return entries.keySet().iterator();
}
}
Associate multiple values with the single key
That is a Multimap:
A collection similar to a Map, but which may associate multiple values with a single key.
LinkedHashMultimap from Google Collections seems to fit the bill:
Implementation of Multimap that does not allow duplicate key-value
entries and that returns collections whose iterators follow the
ordering in which the data was added to the multimap.
If you don't want to add the dependency, you can use a collection as value type:
Map<String /*number*/, List<String> /*names*/> numbers;
Note that the former only allows retrieval in order of insertion, if you want to be able to change it you will have to use the latter hand-rolled solution
Use a Map to store your data. The keys should be String objects and the values should be List objects. Using a List as the map entry value allows to associate multiple values with a single key. A List will also maintain the order of adding elements.
public static void main(String[] args) {
Map<Integer,List<String>> map = new HashMap<Integer,List<String>>();
//insert values into list one
List<String> list1 = new ArrayList<String>();
list1.add("AAA");
list1.add("BBB");
//insert values into list one
List<String> list2 = new ArrayList<String>();
list2.add("CCC");
list2.add("GGG");
//insert values to keys
//single key multiple values
map.put(1234567, list1);
map.put(8765432, list2);
//iterating and displaying the values
for(Map.Entry<Integer,List<String>> entry: map.entrySet() ) {
Integer key = entry.getKey();
List<String> values = entry.getValue();
System.out.println("Value of " +key+ " is " +values);
//System.out.println("Value of " +key+ " is " +values.get(0));
//System.out.println("Value of " +key+ " is " +values.get(1));
}
}

Get key from a HashMap using the value [duplicate]

This question already has answers here:
Java Hashmap: How to get key from value?
(39 answers)
Closed 5 years ago.
I want to get the key of a HashMap using the value.
hashmap = new HashMap<String, Object>();
haspmap.put("one", 100);
haspmap.put("two", 200);
Which means i want a function that will take the value 100 and will return the string one.
It seems that there are a lot of questions here asking the same thing but they don't work for me.
Maybe because i am new with java.
How to do it?
The put method in HashMap is defined like this:
Object put(Object key, Object value)
key is the first parameter, so in your put, "one" is the key. You can't easily look up by value in a HashMap, if you really want to do that, it would be a linear search done by calling entrySet(), like this:
for (Map.Entry<Object, Object> e : hashmap.entrySet()) {
Object key = e.getKey();
Object value = e.getValue();
}
However, that's O(n) and kind of defeats the purpose of using a HashMap unless you only need to do it rarely. If you really want to be able to look up by key or value frequently, core Java doesn't have anything for you, but something like BiMap from the Google Collections is what you want.
We can get KEY from VALUE. Below is a sample code_
public class Main {
public static void main(String[] args) {
Map map = new HashMap();
map.put("key_1","one");
map.put("key_2","two");
map.put("key_3","three");
map.put("key_4","four");
System.out.println(getKeyFromValue(map,"four"));
}
public static Object getKeyFromValue(Map hm, Object value) {
for (Object o : hm.keySet()) {
if (hm.get(o).equals(value)) {
return o;
}
}
return null;
}
}
I hope this will help everyone.
If you need only that, simply use put(100, "one"). Note that the key is the first argument, and the value is the 2nd.
If you need to be able to get by both the key and the value, use BiMap (from guava)
You have it reversed. The 100 should be the first parameter (it's the key) and the "one" should be the second parameter (it's the value).
Read the javadoc for HashMap and that might help you: HashMap
To get the value, use hashmap.get(100).
You mixed the keys and the values.
Hashmap <Integer,String> hashmap = new HashMap<Integer, String>();
hashmap.put(100, "one");
hashmap.put(200, "two");
Afterwards a
hashmap.get(100);
will give you "one"
if you what to obtain "ONE" by giving in 100 then
initialize hash map by
hashmap = new HashMap<Object,String>();
haspmap.put(100,"one");
and retrieve value by
hashMap.get(100)
hope that helps.
public class Class1 {
private String extref="MY";
public String getExtref() {
return extref;
}
public String setExtref(String extref) {
return this.extref = extref;
}
public static void main(String[] args) {
Class1 obj=new Class1();
String value=obj.setExtref("AFF");
int returnedValue=getMethod(value);
System.out.println(returnedValue);
}
/**
* #param value
* #return
*/
private static int getMethod(String value) {
HashMap<Integer, String> hashmap1 = new HashMap<Integer, String>();
hashmap1.put(1,"MY");
hashmap1.put(2,"AFF");
if (hashmap1.containsValue(value))
{
for (Map.Entry<Integer,String> e : hashmap1.entrySet()) {
Integer key = e.getKey();
Object value2 = e.getValue();
if ((value2.toString()).equalsIgnoreCase(value))
{
return key;
}
}
}
return 0;
}
}
If you are not bound to use Hashmap, I would advise to use pair< T,T >.
The individual elements can be accessed by first and second calls.
Have a look at this http://www.cplusplus.com/reference/utility/pair/
I used it here : http://codeforces.com/contest/507/submission/9531943

Categories

Resources