-
Notifications
You must be signed in to change notification settings - Fork 2
Memoization
Memoization is a technique used to speed up repeated computations by caching the results of expensive function calls and returning the cached result when the same inputs occur again. It is a way to trade memory for computation time. For more details, please visit Wikipedia.
In AutoRecord, memoization can be applied to hashCode
, toString
, and any default
method from a interface.
To enable hashCode()
method memoization for a given interface, simply add the @AutoRecord.Options(memoizedHashCode = true)
annotation to it.
This will cause AutoRecord to generate a memoized version of the hashCode()
method along with the equals()
method.
Here's an example interface:
@AutoRecord
@AutoRecord.Options(memoizedHashCode = true)
interface PersonH {
String name();
int age();
}
Here's the corresponding generated record that demonstrates hashCode
memoization:
@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
record PersonHRecord(String name, int age, @Nullable IntMemoizer hashCodeMemoizer) implements PersonH {
PersonHRecord {
requireNonNull(name, "name must not be null");
hashCodeMemoizer = requireNonNullElseGet(hashCodeMemoizer, IntMemoizer::new);
}
PersonHRecord(String name, int age) {
this(name, age, new IntMemoizer());
}
@Memoized
@Override
public int hashCode() {
return hashCodeMemoizer.computeAsIntIfAbsent(this::_hashCode);
}
private int _hashCode() {
return hash(name, age);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (!(other instanceof PersonHRecord)) {
return false;
}
if (hashCode() != other.hashCode()) {
return false;
}
var otherRecord = (PersonHRecord) other;
return Objects.equals(name, otherRecord.name)
&& Objects.equals(age, otherRecord.age);
}
}
An artificial hashCodeMemoizer
recordComponent is introduced to keep memoized value of first _hashCode()
method execution.
To not force user to use main constructor with artificial components, additional constructor is generated with expected parameters only.
To enable toString()
method memoization for a given interface, simply add the @AutoRecord.Options(memoizedToString = true)
annotation to it.
This will cause AutoRecord to generate a memoized version of the toString()
method.
Here's an example interface:
@AutoRecord
@AutoRecord.Options(memoizedToString = true)
interface PersonS {
String name();
int age();
}
Here's the corresponding generated record that demonstrates toString
memoization:
@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
record PersonSRecord(String name, int age, @Nullable Memoizer<String> toStringMemoizer) implements PersonS {
PersonSRecord {
requireNonNull(name, "name must not be null");
toStringMemoizer = requireNonNullElseGet(toStringMemoizer, Memoizer::new);
}
PersonSRecord(String name, int age) {
this(name, age, new Memoizer<>());
}
@Memoized
@Override
public String toString() {
return toStringMemoizer.computeIfAbsent(this::_toString);
}
private String _toString() {
return "PersonSRecord[" +
"name = " + name + ", " +
"age = " + age +
"]";
}
}
An artificial toStringMemoizer
recordComponent is introduced to keep memoized value of first _toString()
method execution.
To not force user to use main constructor with artificial components, additional constructor is generated with expected parameters only.
To enable memoization for a specific default
method, you need to add the @Memoized
annotation to the method in the interface.
This will cause AutoRecord to generate a memoized version of the method.
Here's an example interface:
@AutoRecord
interface PersonM {
String name();
int age();
@Memoized
default long slowComputingMethod() {
return 0L;
}
}
Here's the corresponding generated record that demonstrates defualt
method memoization:
@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
record PersonMRecord(String name, int age, @Nullable LongMemoizer slowComputingMethodMemoizer) implements PersonM {
PersonMRecord {
requireNonNull(name, "name must not be null");
slowComputingMethodMemoizer = requireNonNullElseGet(slowComputingMethodMemoizer, LongMemoizer::new);
}
PersonMRecord(String name, int age) {
this(name, age, new LongMemoizer());
}
@Memoized
@Override
public long slowComputingMethod() {
return slowComputingMethodMemoizer.computeAsLongIfAbsent(PersonM.super::slowComputingMethod);
}
}
An artificial slowComputingMethodMemoizer
recordComponent is introduced to keep memoized value of first original slowComputingMethod()
method execution.
To not force user to use main constructor with artificial components, additional constructor is generated with expected parameters only.