Reputation: 181
Which is faster:
a Set
whose elements are each an instance of CustomObject
as defined below:
public class CustomObject{
private static String A;
private static String B;
private static String C;
public static String getA(){
return A;
}
public static getB(){
return B;
}
public static getC(){
return C;
}
}
a Set
whose elements are each an instance of Map<String, String>
where each Map is of the format {"A" : Avalue, "B" : Bvalue, "C" : Cvalue}
?
or any other data structure that you can think of that better captures this data
if I want to get a subset of all objects/maps who have the attribute/key A == "somevalue"?
You can use .filter, or any other libraries. Also, does the answer change if the set is large?
EDIT: I just had one of my coworkers tell me that the runtime of a custom class is faster than a hashmap-- why would anyone ever use a hashmap, then?
Upvotes: 2
Views: 2137
Reputation: 719229
I'm assuming that we are comparing Map<String, String>
to the equivalent custom type like this:
public class CustomObject {
private String a, b, c;
public CustomObject(String a, String b, String c) {
this.a = a; this.b = b; this.c = c;
}
public String getA() { return a; }
public String getB() { return b; }
public String getC() { return c; }
}
If the operations we are comparing are obj.getA()
versus map.get("A")
, then the custom map will be faster, probably by 1 to 2 orders of magnitude. Yes ... a lot faster.
On the other hand, if we put the CustomObject
instances into a set of "mixed type" objects, whose fields we don't know anything about, then calling getA
becomes much more difficult / expensive, and the Map
solution is certainly simpler and possible faster too. (It depends on the assumptions you can make.)
Also, does the answer change if the set is large?
No. It doesn't change the performance characteristics significantly.
Why would anyone ever use a hashmap, then?
The use-cases where it is better / necessary to use a Map
are when the set of possible keys are not known at compile time. This means that you can't write the CustomClass
as a regular class with hand-written source code.
In fact, in most cases it is the relative code simplicity and robustness of the two approaches that should decide the approach you take. If the keys are static, the obj.getA()
approach isn't just faster. It is also more robust, because you can't accidentally write something like map.get("a")
instead of map.get("A")
... which will return an unexpected null
and could lead to an NPE. If the keys are dynamic / not known at compile time, the map.get("A")
is simpler and probably more robust.
Upvotes: 2
Reputation: 199264
Java class is faster.
I just had one of my coworkers tell me that the runtime of a custom class is faster than a hashmap-- why would anyone ever use a hashmap, then?
Because you might be interested in storing dynamically generated values and/or don't know in advance what attribute your class will have.
Upvotes: 1
Reputation: 7242
If you are looking for optimal runtime performance in repeatedly searching for which sets contain which values for specific attributes then you either need to use a database (or an in-memory one) or manually denormalize your data such that you maintain a map of lists for each attribute, keyed by its value. Note that this solution is only worthwhile if these lookups are frequent enough and the performance critical enough to warrant the data duplication costs.
E.g.
// aSets is a map to a list of all the sets which have
// a specified value for field "_A" (values being the map key)
Map<String, List<Set<MyObject>>> aSets;
Map<String, List<Set<MyObject>>> bSets;
...
It is not ideal, but it provides the lookups you require.
It is not too difficult to write an indexer class of sorts that creates these maps automatically using reflection on the underlying object of a supplied collection. I wrote some code to do something like this awhile ago as a proof of concept for something or other. Never used it but kept it around... this is uncommented and use at your own risk (and it does not work on Collections as written but would be easy to modify):
public class Indexer
{
private Map<String,Map<Object,Set<Object>>> index = new HashMap<String,Map<Object,Set<Object>>>();
public void add(Object object) throws Exception
{
BeanInfo info = Introspector.getBeanInfo(object.getClass());
PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
for (PropertyDescriptor descriptor : propertyDescriptors)
{
String fieldName = descriptor.getName();
Map<Object,Set<Object>> map = index.get(fieldName);
if (map == null)
{
map = new HashMap<Object,Set<Object>>();
index.put(fieldName, map);
}
Method method = descriptor.getReadMethod();
Object data = method.invoke(object);
Set<Object> set = map.get(data);
if (set == null)
{
set = new HashSet<Object>();
map.put(data, set);
}
set.add(object);
}
}
public Set<Object> get(String fieldName, Object value)
{
Map<Object,Set<Object>> map = index.get(fieldName);
if (map != null)
{
Set<Object> set = map.get(value);
if (set != null)
{
return Collections.unmodifiableSet(set);
}
}
return null;
}
public static class Test
{
private String value1;
private String value2;
public int number;
public Test(String v1, String v2, int n)
{
value1 = v1;
value2 = v2;
number = n;
}
public String getValue1()
{
return value1;
}
public void setValue1(String value1)
{
this.value1 = value1;
}
public String getValue2()
{
return value2;
}
public void setValue2(String value2)
{
this.value2 = value2;
}
@Override
public String toString()
{
return "Test [value1=" + value1 + ", value2=" + value2 + ", number=" + number + "]";
}
}
public static void main(String[] args) throws Exception
{
Test test1 = new Test("blue", "dog", 5);
Test test2 = new Test("blue", "cat", 10);
Test test3 = new Test("green", "duck", 10);
Indexer indexer = new Indexer();
indexer.add(test1);
indexer.add(test2);
indexer.add(test3);
System.out.println("get value1 = blue: \n" + indexer.get("value1", "blue"));
System.out.println("\nget value1 = green: \n" + indexer.get("value1", "green"));
System.out.println("\nget number = 5: \n" + indexer.get("number", 5));
System.out.println("\nget number = 10: \n" + indexer.get("number", 10));
}
}
Upvotes: 0