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"));
Related
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.
I've seen other questions about getting objects from Set's based on index value and I understand why that is not possible. But I haven't been able to find a good explanation for why a get by object is not allowed so thought I would ask.
HashSet is backed by a HashMap so getting an object from it should be pretty straightforward. As it is now, it appears I would have to iterate over each item in the HashSet and test for equality which seems unnecessary.
I could just use a Map but I have no need for a key:value pair, I just need a Set.
For example say I have Foo.java:
package example;
import java.io.Serializable;
public class Foo implements Serializable {
String _id;
String _description;
public Foo(String id){
this._id = id
}
public void setDescription(String description){
this._description = description;
}
public String getDescription(){
return this._description;
}
public boolean equals(Object obj) {
//equals code, checks if id's are equal
}
public int hashCode() {
//hash code calculation
}
}
and Example.java:
package example;
import java.util.HashSet;
public class Example {
public static void main(String[] args){
HashSet<Foo> set = new HashSet<Foo>();
Foo foo1 = new Foo("1");
foo1.setDescription("Number 1");
set.add(foo1);
set.add(new Foo("2"));
//I want to get the object stored in the Set, so I construct a object that is 'equal' to the one I want.
Foo theFoo = set.get(new Foo("1")); //Is there a reason this is not allowed?
System.out.println(theFoo.getDescription); //Should print Number 1
}
}
Is it because the equals method is meant to test for "absolute" equality rather than "logical" equality (in which case contains(Object o) would be sufficient)?
Java Map/Collection Cheat Sheet
Will it contain key/value pair or values only?
1) If it contains pairs, the choice is a map. Is order important?
. 1-1) If yes, follow insertion order or sort by keys?
. . 1-1-1) If ordered, LinkedHashMap
. . 1-1-2) If sorted, TreeMap
. 1-2) If order is not important, HashMap
2) If it stores only values, the choice is a collection. Will it contain duplicates?
. 2-1) If yes, ArrayList
. 2-2) If it will not contain duplicates, is primary task searching for elements
(contains/remove)?
. . 2-2-1) If no, ArrayList
. . 2-2-2) If yes, is order important?
. . . 2-2-2-1) If order is not important, HashSet
. . . 2-2-2-2) If yes, follow insertion order or sort by values?
. . . . 2-2-2-2-1) if ordered, LinkedHashSet
. . . . 2-2-2-2-2) if sorted, TreeSet
A Set is a Collection of objects which treats a.equals(b) == true as duplicates, so it doesn't make sense to try to get the same object you already have.
If you are trying to get(Object) from a collection, a Map is likely to be more appropriate.
What you should write is
Map<String, String> map = new LinkedHashMap<>();
map.put("1", "Number 1");
map.put("2", null);
String description = map.get("1");
if an object is not in the set (based on equals), add it, if it is in the set (based on equals) give me the set's instance of that object
In the unlikely event you need this you can use a Map.
Map<Bar, Bar> map = // LinkedHashMap or ConcurrentHashMap
Bar bar1 = new Bar(1);
map.put(bar1, bar1);
Bar bar1a = map.get(new Bar(1));
If you want to know that new Foo("1"); object is already present in the set then you need to use contains method as:
boolean present = set.contains(new Foo("1"));
The get kind of method i.e. set.get(new Foo("1")); is not supported because it doesn't make sense. You are already having the object i.e. new Foo("1") then what extra information you would be looking through get method.
Your last sentence is the answer.
get(Object o) would run through the HashSet looking for another object being equal to o (using equals(o) method). So it is indeed the same as contains(o), only not returning the same result.
HashSet is a little bit simplier than HashMap. If you don't need the features of HashMap, why use it? If the method like getObject(ObjectType o) was implemented by Java we dont need to iterate over the set after calling contain() methode...
The reason why there is no get is simple:
If you need to get the object X from the set is because you need something from X and you dont have the object.
If you do not have the object then you need some means (key) to locate it. ..its name, a number what ever. Thats what maps are for right.
map.get( "key" ) -> X!
Sets do not have keys, you need yo traverse them to get the objects.
So, why not add a handy get( X ) -> X
That makes no sense right, because you have X already, purist will say.
But now look at it as non purist, and see if you really want this:
Say I make object Y, wich matches the equals of X, so that set.get(Y)->X. Volia, then I can access the data of X that I didn have. Say for example X has a method called get flag() and I want the result of that.
Now look at this code.
Y
X = map.get( Y );
So Y.equals( x ) true!
but..
Y.flag() == X.flag() = false. ( Were not they equals ?)
So, you see, if set allowed you to get the objects like that It surely is to break the basic semantic of the equals. Later you are going to live with little clones of X all claming that they are the same when they are not.
You need a map, to store stuff and use a key to retrieve it.
if you only want know what are in the Hashset, you can use .toString(); method to display all Hashset Contents separated by comma.
A common use case of a get method on Set might be to implement an intern set. If that's what you're trying to achieve, consider using the Interner interface and Interners factory from Google Guava.
I've got the same problem as the thread author and I've got a real reason why
a Set should have a get method:
I overwrote equals of e.g. X, the content of the set Set and so the contained
object is not necessarily the same as the checked one. In my scenario I'll remove
semantic doubles in an other collection and enrich the "original" with some relations
of the "double" so I need the "original" to be able to drop the double.
get(Object o) is useful when we have one information linked to other information just like key value pair found in HashMap .So using get() method on one information we can get the second information or vice-versa.
Now, if HashSet provides get(Object o) method you need to pass an object. So if you have the object to pass to the get(Object o) method that means you already have the object, then what is need of get(Object o) method.
As everyone mentioned before, there is no such method and for good reasons. That being said, if you wish to get a certain object from a HashSet in java 8 using a one-liner (almost), simply use streams. In your case, it would be something like:
Foo existing = set.stream().filter(o -> o.equals(new Foo("1"))).collect(Collectors.toList()).iterator().next();
Note that an exception will be thrown if the element doesn't exist so it is technically not a one-liner, though if the filter is properly implemented it should be faster than a traditional iteration over the collection elements.
I have a Map like this :
Map<String,GridCell> cellsMap
I pass this into a method and the return from that method should contain a Map(say answerMap) which contains all the entries of cellsMap map plus an extra entry that contains a String as the key and a String as the value . Something like :
Map<String,Object> answerMap = new ConcurrentHashMap<String,Object>();
//answer should first contain all the map entries of cellsMap and then add an extra entry like the following
answer.put(getId(), getSelectionValue()); // getSelectionValue() returns a String that contains coordinates of the selected cells.
return answerMap;
Have you considered the Map.putAll() method ?
e.g.
answerMap.putAll(cellsMap);
I don't think this is a good object model, by the way. I think you're better off creating a new class that contains your original map (maybe a copy) and an additional field for your String/String pair.
Otherwise you're throwing objects of different types into the same map, and that's going to make life complicated when you later extract that info. Each time you extract via a key you're going to have to check the type of the object returned. Note that ConcurrentHashMaps don't maintain insertion order.
Use clone() method.
HashMap answerMap = (HashMap)cellsMap.clone();
Map interface has putall() method which add all values of another object in Map.
I have a need to store a list of dynamically created objects in a way where they can all be retrieved and their methods called on demand.
As far as I can see for the list and creation, a HashMap fits my needs but i'm a bit puzzled on recalling the objects and calling their methods using the HashMap.
Just as a reference, let me give you a little code:
Here is the HashMap:
Map<String, Object> unitMap = new HashMap<String, Object>();
// here is how I put an object in the Map notice i'm passing coordinates to the constructor:
unitMap.put("1", new Worker(240, 240));
unitMap.put("2", new Worker(240, 240));
Now I need to create a method that retrieves every object in the hashmap and call a method from each object. is this possible or can the created objects only be referenced directly. If so, is there another way to call a method of all existing instances of a class dynamically (in other words, on user input)?
Sure. You can do this:
for (Object thing : unitMap.values()) {
// use "thing" here
}
If you need the keys too, you can either get just the keys:
for (String key : unitMap.keySet()) {
// use "key" here
}
or both the keys and values together:
for (Map.Entry<String, Object> entry : unitMap.entrySet()) {
// use "entry.getKey()" and "entry.getValue()"
}
In all the above cases, each entry in the map is traversed one by one. So at the end of the loop, you'll have processed all the entries in the map.
If all of the values in the Map are Worker objects, you should declare your map to be of type Map<String, Worker>. This way, when you pull a value out of the map, it will be typed as a Worker. This way you can call any method declared on Worker as opposed to having to check the type at runtime using instanceof.
If the map holds different values, and you need to keep the value type as Object, it may be advantageous to use an interface to define the method that you want to call for each different object type.
If you do not know what method you want to run on the values until runtime, and the map can hold different values, you will just have to do what you are currently doing, and use Map<String, Object>.
Finally, to get the values of the map, you do just as Chris Jester-Young mentioned before me. The biggest advantage, as I said previously, is that your objects will be typed, and you will have no need for casting/instanceof checking.
I use this to put all values from hashMap on a List, hope it helps.
private List<String> getValuesFromHashMap(HashMap<String, String> hashMap) {
List<String> values = new ArrayList<String>();
for (String item : hashMap.values()) {
values.add(item);
}
return values;
}
I have some objects with Date parameters. What collection will be best for storing them and later querying for object/objects with particular date ? (like given as a String or java.util.Date format) ?
EDIT:
I was trying to use TofuBear's solution, but cannot make it work. let's say I am calling my function (which returns Map) with a list of objects, and Date object. What next ? I was trying different methods but everything is just bloody red from NetBeans's errors:
public Map<Date, List<Person>> createDateList(Date date, List<Person> list){
Map<Date, List<Person>> map = null;
}
This however does not solve problem of querying, cuz I'm just creating a map with one object. I need to have a list of all objects (which have Date field) and their dates in a map. Am I thinking correctly ?
Probably a Map<Date, WhateverTypeYouWant> or Map<Date, List<WhateverTypeYouWant>> if there are multpile values with the same date.
Then you would add them something like this:
map.put(object.getDate(), object);
Edit based on the comment:
For the List version I use something like this (untested from memory... but pretty sure it is right):
List<WhateverTypeYouWant> list;
list = map.get(object.getDate())
if(list == null)
{
list = new ArrayList<WhateverTypeYouWant>();
map.put(object.getDate(), list);
}
list.add(object);
Sounds like a Map<Date, YourObject> (or Map<String, YourObject> if you prefer so) would do the job.
Maps come in different flavours, the most generally used is HashMap.
Map<Date, Other>, as others have said, but if you are interested in more than getting an entry that matches a given date exactly then you would want to look into using a NavigableMap. A navigable map will allow you to get entries that are close to what you are searching for if nothing matches exactly.
If you use Map<Date, SomeObject> like other have suggested, you will only be able to do exact searches and in case you change Date inside SomeObject you'll need to manually update the Map. Even more work if you choose to use Map<Date, List<SomeObject>>.
Instead use List<SomeObject> and use Collections.binarySearch(). This requires Collection to be sorted and you need to write custom java.util.Comparator.
private class SomeObjectComparator implements Comparator<SomeObject> {
#Override
public int compare(SomeObject o1, SomeObject o2) {
// this breaks equality rule for Set
// do not use in Sets
return o1.date.compareTo(o2.date);
}
}
then use it like this (preferably wrap it in helper method):
List<SomeObject> someList = new ArrayList<SomeObject>
Comparator comparator = new SomeObjectComparator();
Collections.sort(someList, comparator);
int resultIndex = Collections.binarySearch(someList, someSearchedObject, comparator)
Given this comparator, binarySearch() will only search by Date not by other properties of SomeObject.
Also look for meaning of resultIndex in Collections.binarySearch()
In side the collection API stored the reference addresses of the attribute (of like list, linked List ) for which required the boxing on primitive data type element, in its correspondence Wrapper class type object and that one object are automatically up-castes to Object type class (Object class is the Super class of every Class) And this reference object can pass for any other program (Inside same or different Package) with the Multiple Resection on the OWN date (Like only reading, removing, Updating, Reading only one time, adding more element inside list) according with requirement. for which reference object is only usefull to avoid multiple problems.