Reputation: 301
I'm using a custom class Foo
in Java as the key type in a HashMap
. All the fields of Foo
instances are immutable (they are declared final and private and are assigned values only in the constructor). Thus, the hashCode()
of a given Foo
object is also fixed, and for optimization purposes, I am calculating it in the constructor and simply returning that value in the hashCode()
method.
Instances of Foo
also have a value()
method which returns a similar fixed value once the object has been instantiated. Currently I am also calculating it in the constructor and returning it in the method, but there is a difference between hashCode()
and value()
: hashCode()
is called for the first time almost instantly after the object is created, but value()
is called much later. I understand that having a separate Thread to calculate the hash-code would simply increase the run-time because of synchronization issues, but:
value()
? Would it improve run-time at all?Threads
enough, or do I need to use pools etc.?Note: this may seem like I'm optimizing the wrong parts of my program, but I've already worked on the 'correct' parts and brought the average run-time down from ~17 seconds to ~2 seconds. Edit: there will be upwards of 5000 Foo
objects, and that's a conservative estimate.
Upvotes: 4
Views: 517
Reputation: 18148
I recommend creating a Future for value
- create a static fixedTheadPool and submit the value
calculations on it. This way there's no risk that value
will be accessed before it's available - the worst case is that whatever is accessing value
will block on a Future.get call (or use the version with a timeout if e.g. deadlock is a concern)
Because Future.get
throws checked exceptions which can be a nuisance, you can wrap the get
call in your class's getter method and wrap the checked exceptions in a RuntimeException
class MyClass {
private static final ExecutorService executor = Executors.newFixedThreadPool(/* some value that makes sense */);
private final Future<Value> future;
public MyClass() {
future = executor.submit(/* Callable */);
}
public boolean isValueDone() {
return future.isDone();
}
public Value value() {
try {
return future.get();
} catch(InterruptedException|ExecutionException e) {
throw new RuntimeException(e);
}
}
}
Upvotes: 1
Reputation: 26868
It definitely sounds like deferred calculation is a good approach here - and yes, if you create a lot of these objects, a thread pool is the way to go.
As for value()
's return value until it's ready, I would stay away from returning invalid values, and instead either make it blocking (and add some isValueReady()
helper) or make it instantly return a "future" - some object that offers those same isReady
and a blocking get
methods.
Also, never rely on "much later" - always make sure the value there is ready before using it.
Upvotes: 2