I have a HashMap defined with something like this:
Map<Foo, List<Bar>> = new HashMap();
I am trying to do a reverse search of the Hashmap using the Bar to get the Foo.
I am wanting to do something like this:
if(ArrayListBar.contains(bar)) {
return Foo;
} else {
return null;
}
Is this achievable in HashMap or is there a better way to deal with this without using the HashMap?
You can do it with Map iteration.
private Foo getKeyByValue(Map<Foo, List<Bar>> map, Bar bar){
for (Map.Entry<Foo, List<Bar>> entry : map.entrySet()){
if (entry.getValue().contains(bar)){
return entry.getKey();
}
}
return null;
}
You iterate for each entry on the map and you return the Key when the array list contains the entered bar value.
Note that your Bar class should implement the equals method so the entry.getValue().contains(bar) can be evaluated if the bar in the List with the bar on the method input are different objects.
Update: Added missing return null statement when no map element is found.
The best approach can be different under different circumstances.
You can do it by iterating the map and checking whether the list of each entry contains the value or not. But that's Ok only if the map is not too big and the lists in it or either not too big or sorted.
But if the size of the map is big or the lists are big and not sorted and you need to do multiple lookups, it is better to create a reverse map Map.
In that case you have to make sure the hashkey and equals method of Bar are implemented correctly.
If the same value (Bar) can be in multiple lists for different keys (Foo), the reverse map might also require a list of values: Map>.
Depending on the situation, you can create the reverse map while building the original map or afterwards when doing the first lookup and cache it for reuse in later lookups.
When thread safety is involved, it is preferred to create it when creating the original map because in that case you don't need to worry about thread safety of the lookup method which is a problem when creating the reverse map during the first lookup.
Related
I basically need to know if my HashMap has different keys that map to the same value. I was wondering if there is a way other than checking each keys value against all other values in the map.
Update:
Just some more information that will hopefully clarify what I'm trying to accomplish. Consider a String "azza". Say that I'm iterating over this String and storing each character as a key, and it's corresponding value is some other String. Let's say I eventually get to the last occurrence of 'a' and the value is already be in the map.This would be fine if the key corresponding with the value that is already in the map is also 'a'. My issue occurs when 'a' and 'z' both map to the same value. Only if different keys map to the same value.
Sure, the fastest to both code and execute is:
boolean hasDupeValues = new HashSet<>(map.values()).size() != map.size();
which executes in O(n) time.
Sets don't allow duplicates, so the set will be smaller than the values list if there are dupes.
Very similar to EJP's and Bohemian's answer above but with streams:
boolean hasDupeValues = map.values().stream().distinct().count() != map.size();
You could create a HashMap that maps values to lists of keys. This would take more space and require (slightly) more complex code, but with the benefit of greatly higher efficiency (amortized O(1) vs. O(n) for the method of just looping all values).
For example, say you currently have HashMap<Key, Value> map1, and you want to know which keys have the same value. You create another map, HashMap<Value, List<Key>> map2.
Then you just modify map1 and map2 together.
map1.put(key, value);
if(!map2.containsKey(value)) {
map2.put(value, new ArrayList<Key>);
}
map2.get(value).add(key);
Then to get all keys that map to value, you just do map2.get(value).
If you need to put/remove in many different places, to make sure that you don't forget to use map2 you could create your own data structure (i.e. a separate class) that contains 2 maps and implement put/remove/get/etc. for that.
Edit: I may have misunderstood the question. If you don't need an actual list of keys, just a simple "yes/no" answer to "does the map already contain this value?", and you want something better than O(n), you could keep a separate HashMap<Value, Integer> that simply counts up how many times the value occurs in the map. This would take considerably less space than a map of lists.
You can check whether a map contains a value already by calling map.values().contains(value). This is not as efficient as looking up a key in the map, but still, it's O(n), and you don't need to create a new set just in order to count its elements.
However, what you seem to need is a BiMap. There is no such thing in the Java standard library, but you can build one relatively easily by using two HashMaps: one which maps keys to values and one which maps values to keys. Every time you map a key to a value, you can then check in amortized O(1) whether the value already is mapped to, and if it isn't, map the key to the value in the one map and the value to the key in the other.
If it is an option to create a new dependency for your project, some third-party libraries contain ready-made bimaps, such as Guava (BiMap) and Apache Commons (BidiMap).
You could iterate over the keys and save the current value in the Set.
But, before inserting that value in a Set, check if the Set already contains that value.
If this is true, it means that a previous key already contains the same value.
Map<Integer, String> map = new HashMap<>();
Set<String> values = new HashSet<>();
Set<Integter> keysWithSameValue = new HashSet<>();
for(Integer key : map.keySet()) {
if(values.contains(map.get(key))) {
keysWithSameValue.add(key);
}
values.add(map.get(key));
}
I will state up front this is NOT a homework problem and is part of a hobby project I am working on. I have a working solution but I feel as though my solution is messy and space inefficient.
I have a list of items.
The length of the list is variable. Items are removed by index. Items are added to the end of the list. Items can repeat.
When an item is looked up, it is just a check to see if it is already in the list. Each item has a item-unique 'key'.
Items are sorted based on a user assigned priority level.
Right now I am using two data structures to accomplish my goals.
I have an ArrayList that is sorted based on priority (for keeping order), and an ArrayList that is sorted based on the item key (for fast lookup).
Is there a single data structure that will solve this problem? I am coding in Java if that matters. A sorted set is almost what I want, but because there are repeats I don't think this will work
Since you want lookup by key you should use a Map of some sort.
Since you want multiple objects associated with the same key you need a map of key to a list of objects rather than a map of key to a single object.
I would use a TreeMap so that it can be sorted, where the key is your key and the value is an ArrayList for the list of objects that should be returned for that key.
You will have to implement a comparator in order to get the sorting you want.
One thing to consider when using a Map of Lists is that you need to check if the map already contains a list for that key when you want to add an item.
E.g. if your map looks like this:
Map<String, List<Object>> map = new TreeMap<String, ArrayList<Object>>();
Adding an object looks like this:
public void addObject(String key, Object object) {
List<Object> objects = map.get(key);
if (objects == null) {
objects = new ArrayList<Object>();
map.put(key, objects);
}
objects.add(object);
}
What you're looking for is called a compound key in an RDBMS. If you create an ArrayList using the compound key (priority, key), you will have a single data structure.
I'm not sure about a single data structure but you can use an ArrayList<> in conjunction with a HashSet<>
Create a wrapper class that wraps an Item and a priority which I assume is an int value.
class ItemWithPriority
{
private Item item;
private int priority;
ItemWithPriority(Item item, int priority)
{
this.item = item;
this.priority = priority;
}
}
Maintain an ArrayList<ItemWithPriority> that is always sorted based on a custom Comparator<ItemWithPriority> which compares based on priority.
Additionally maintain a HashSet<Item> that contains the set of all items at a given point in time.
Insert:
itemWithPriorityList.add(new ItemWithPriority(item, priority));
Collections.sort(itemWithPriorityList, new PriorityBasedComparator());
itemSet.add(item);
Remove: (Based on index)
Item itemToBeRemoved = itemWithPriorityList.get(index);
itemWithPriorityList.remove(index);
itemSet.remove(itemToBeRemoved);
Lookup:
itemSet.contains(item)
Here is my problem (simplified):
Suppose we have a class:
public class MyClass{
String name;
Double amount;
String otherAttribute;
}
And a List<MyClass> myList
Suppose we have 2 elements from myList. Let's say object1 and object2
What I would like to do is:
if (object1.name.equals(object2.name){
//add amount of object2 to object1
//remove object 2 from the list
}
Considering I have a large list (maybe 100 elements) and I would like to find the best and less consuming way to do what I want.
What would you suggest ?
EDIT:
Yes 100 items is not large, but I would call this method (of merging similar objects) many times for many different sized lists. So that's way I would like to find the best practice for this.
I can't override equals or hashCode methods of MyClass, unfortunately (client requirement)
I'd add the objects to a HashMap where the name is the key and MyClass is the value being stored. Loop through each object in your list to add them to the map. If the name isn't in the map, just add the name, object pair. If it is already in the map, add the amount to the object already stored. When the loop completes, extract the objects from the map.
100 elements is a tiny size for a list, considering you're not going to repeat the operation some hundreds of thousands times. If it's the case, I'd consider creating a data structure indexing the list items by the search property (Map for instance), or ordering it if suitable and using an efficient search algorithm.
One approach (as suggested by Bill) would be to traverse the List adding every element to a Map, with the name property as key. You can take advantage of put's return to know if a name has been previously put into the map, and add the previosuly accumulated amounts in the current element. Finally, you could use values() to get the List without duplicates.
For instance:
List<MyClass> l;
Map<String, Myclass> m = new HashMap<MyClass>();
for (MyClass elem : l) {
MyClass oldElem = m.put(elem.getName(), elem);
if (oldElem != null) {
elem.setAmount(elem.getAmount() + oldElem.getAmount());
}
}
l = new ArrayList<MyClass>(m.values());
If you need to preserve order in the list, consider using a LinkedHashMap.
This is an O(n^2) problem unfortunately. You need to compare n elements to n-1 other elements. There is no way to do this but to brute force it.
If you used a HashMap however, you could check the map for an element before adding it to the Map which is an O(1) operation. It would look something like this:
HashMap<String, MyClass> map = new HashMap<String, MyClass>();
when you add an element:
if (map.get(obj1.name) != null) {
var obj2 = map.get(obj1.name);
obj2.amount = obj2.amount + obj1.amount;
map.put(obj1.name, obj2);
}
'Large' is relative, 100 items is definitely not large, imagine if you had to process a stream of 1.000.000 items/second. Then you would redefine large :D
In your example, what I think would be good to avoid would be to create a Set of your items' names. Searching a java HashSet takes O(1), so if an objects' name exists in the hash set, then update it on the list. An even better solution would be to create a HashMap, on which you could say e.g.
if(mymap.contains(thename)){
mymap.put(thename, newSum);
}
this being an example of how you could use it. Here's a link to get you started: http://java67.blogspot.gr/2013/02/10-examples-of-hashmap-in-java-programming-tutorial.html
I suggest to optimize (if possible) by not even doing the .add() to the list if an element with the same name exists. Using one of the hash based collections in combination with a proper equals() & hashCode() implementation based on MyClass.name should also give you somewhat good performance.
First, since you cannot override equals or hashCode, then you need to have the function that will do this functionality in the same package as your MyClass class, since no accessor methods are defined in MyClass
Second, try to have your items in a LinkedList, so that you can remove repeating elements from that list really quick without having to move around the other items.
Use a map to keep track of the amount that corresponds to a given name, while iterating the list, and removing repeating elements at the same time. In this way you don't have to create a new list.
List<MyClass> myClass_l;
Map<String, MyClass> nameMyClass_m = new HashMap<String, MyClass>();
for (Iterator<MyClass> iterator = myClass_l.iterator(); iterator.hasNext(){
MyClass m = iterator.next();
if (nameAmount_m.contains(m.name)){
MyClass firstClass = m.get(m.name);
firstClass.amount += m.amount;
iterator.remove();
}
else{
nameMyClass_m.put(m.name, m);
}
}
By the time you have finished the loop, you will have the items you want in your original list.
Needs:
Storing objects of a class which overrides equals and hash code
Will be looping and shoving objects into the datastructure
Need to be able to call contains to check whether a certain object is stored in the structure
If contains returns true then fetch that specific object from the structure and call a certain getter on that object
Options I've considered:
Map - this works for all the needs but I don't really have a map (key and a value). all I have is bunch of objects. Would it be a good practice to forcefully use a map by storing objects as key and integer or something in the value?
Set would work, however, it doesn't have a fetch method like get.
List would also work, but it doesn't have a method to fetch that is non index based. Meaning, once contains returns true I'll have to loop through the list to find the index of my particular object and then fetch it.
I'm open to using different libraries like apache commons or guava for example.
List would also work, but it doesn't have a method to fetch that is non index based.
List has an indexOf(Object) method which will do exactly what you want.
Although the best thing to use in this scenario would be a Map, because it offers fast retrieval based on Key-Value pair.
But List also allows to fetch data based on index.
So, you can use either a List or a Map. But to make your task easier, I would prefer a Map. Because i case of Map you won't have to search for an index of an Object, then get the Object at that index. Fetching is just a one-line operation.
// When using a List.
List<String> myList = new ArrayList<String>();
if (myList.contains("rohit")) {
myList.get(myList.indexOf("rohit"));
}
// When using Map.
Map<String, String> myMap = new HashMap<String, String>();
// You can directly fetch your object, based on some Key if you have one..
myMap.get("key");
You need a set. You don't need a fetch method (you think you do), because like you said you only have a bunch of objects. And since these use equals and hashCode, a set is exactly what you need.
Of course a map could do as well, because its keys is a set as well, but in the end you need to better specify your requirements, as it appears you are a bit confused as to the purpose of your data structure. From what I understand, you do not need a map indeed.
A hash set implementation will do. Here is what you can do with it all:
class Foo
{
final String name;
Foo(String name)
{
this.name = name;
}
boolean equals(Object obj)
{
return (obj instanceof Foo) && ((Foo)obj).name.equals(name);
}
}
Set<Foo> fooSet = new HashSet<Foo>();
fooSet.add(new Foo("someFoo"));
assert fooSet.contains(new Foo("someFoo"));
So i have a map that i created (inserted data) in an order i wanted.
When parsing the map the 1st key returned in foreach is not the first key i inserted.
Is there a way for that to happen?
Also sorting my map is kinda tricky cause it has to be sorted by Value and in specific field within the Value.
Ty
Check out LinkedHashMap for a Map implementation with predictable iteration order. You also might consider just using a List if you're not actually doing lookup by keys.
Let's see. Your requirements seem to be:
You have a set of key / value pairs, where the keys are unique.
You want to be able to do fast lookup of the value for a given key.
You want to be able to iterate over the keys (or pairs) in insertion order.
You want to be able to iterate over the values in order of some field of the value type.
There is no single standard Java collection class that satisfies all of these requirements. And I don't think that Commons collections or Google collections would either ...
If you were to throw out requirement 3, then a TreeSet (instantiated with a custom Comparator) would do the job. If you were to throw out requirement 4, then a LinkedHashMap would do the job.
To satisfy all requirements you need to do one of the following:
Use a LinkedHashMap, and when you want to iterate in some order dependent on the values extract the map's values collection, sort it using your custom comparator, and
return an iterator for the sorted collection.
Use both a LinkedHashMap and a TreeMap, and update the two in parallel.
Create a custom fascade class for a LinkedHashMap and a TreeMap. This needs to keep both data structures up to date when you call put, remove etcetera, and also provide extra methods for getting the sorted values.
If you can sort your items up front by the value attribute, then you can use a LinkedListHashMap, since that preserves the order you specify. However, this seems a bit fragile, and is not suitable if you need to later add more items to the map.
The alternative is to store the values in a list, sorted as you need, and use binary search to retrieve items and find the insertion point for new items.
You can even wrap all this and put it behind a Map interface.
The Collections class provides binarySearch. Here's an outline:
Put your Value class in a list, List<Value> values.
Implement a Comparable<Value> class that compares values using the attribute you want to sort them on.
Use Comparator<Value> to sort the list.
Now that the list is sorted, you can use Collections.binarySearch(values, aValue, Comparator<Value>) to find the index of the actual value. Note that aValue isn't a real value - it's a value with the attributes set to provide the key, but the rest of it is uninitalized. The aValue is only used to hold the sort key.
In code
List<Value> values = new ArrayList<Values>();
// .. add values
values.add(new Value(key, data1, data2, etc..));
Comparator<Value> compValue = new Comparator<Value>() {
public int compare(Value v1, Value v2) {
return v1.getKey()>v2.getKey();
}
}
Collections.sort(values, compValue);
// now we can search on key
int index = Collections.binarySearch(values, new Value(keyTofind), valueComp);
Value foundValue = null; // value with the key may not be in the list
if (index>=0)
foundValue = values.get(index);
// we can also update the list
Value newValue = new Value(key, data, data2, etc...);
int insert = Collections.binarySearch(values, newValue, valueComp);
// insert will be negative
values.add((-insert)-1, newValue);
EDIT: If you wrap this up in a Map interface, e.g. extending AbstractMap, it will be serializable.