-
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
memoization for a given interface, simply add the @AutoRecord.Options(memoizedHashCode = true)
annotation to it.
This will cause AutoRecordd to generate a memoized version of the hashCode
method along with the equals
method.
Here's an example interface:
import pl.com.labaj.autorecord.AutoRecord;
@AutoRecord
@AutoRecord.Options(memoizedHashCode = true)
interface Person {
String name();
int age();
}
Here's the corresponding generated record that demonstrates hashCode
memoization:
import static java.util.Objects.hash;
import static java.util.Objects.requireNonNull;
import java.lang.Integer;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.annotation.processing.Generated;
import pl.com.labaj.autorecord.Memoized;
import pl.com.labaj.autorecord.Memoizer;
@Generated("pl.com.labaj.autorecord.AutoRecord")
record PersonRecord(String name, int age, @Nullable Memoizer<Integer> hashCodeMemoizer) implements Person {
PersonRecord {
requireNonNull(name, () -> "name must not be null");
}
PersonRecord(String name, int age) {
this(name, age, new Memoizer<>());
}
@Memoized
@Override
public int hashCode() {
return hashCodeMemoizer.computeIfAbsent(() -> _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 PersonRecord)) {
return false;
}
if (hashCode() != other.hashCode()) {
return false;
}
var otherRecord = (PersonRecord) other;
return Objects.equals(name, otherRecord.name)
&& Objects.equals(age, otherRecord.age);
}
}
An artificial hashCodeMemoizer
component is introduced to keep memoized value of first _hashCode()
method execution.
To allow user not to use main constructor with it, additional constructor is generated with parameters recognized as components from the interface.
To enable toString
memoization for a given interface, simply add the @AutoRecord.Options(memoizedToString = true)
annotation to it.
This will cause AutoRecordd to generate a memoized version of the toString
method.
Here's an example interface:
import pl.com.labaj.autorecord.AutoRecord;
@AutoRecord
@AutoRecord.Options(memoizedToString = true)
interface Person {
String name();
int age();
}
Here's the corresponding generated record that demonstrates toString
memoization:
import static java.util.Objects.requireNonNull;
import java.lang.Override;
import java.lang.String;
import javax.annotation.Nullable;
import javax.annotation.processing.Generated;
import pl.com.labaj.autorecord.Memoized;
import pl.com.labaj.autorecord.Memoizer;
@Generated("pl.com.labaj.autorecord.AutoRecord")
record PersonRecord(String name, int age, @Nullable Memoizer<String> toStringMemoizer) implements Person {
PersonRecord {
requireNonNull(name, () -> "name must not be null");
}
PersonRecord(String name, int age) {
this(name, age, new Memoizer<>());
}
@Memoized
@Override
public String toString() {
return toStringMemoizer.computeIfAbsent(() -> _toString());
}
private String _toString() {
return "PersonRecord[" +
"name = " + name + ", " +
"age = " + age +
"]";
}
}
An artificial toStringMemoizer
component is introduced to keep memoized value of first _toString()
method execution.
To allow user not to use main constructor with it, additional constructor is generated with parameters recognized as components from the interface.
To enable memoization for a specific default
method, you need to add the @Memoized
annotation to the method in the interface.
This will cause AutoRecordd to generate a memoized version of the method.
Here's an example interface:
import pl.com.labaj.autorecord.AutoRecord;
import pl.com.labaj.autorecord.Memoized;
@AutoRecord
interface Person {
String name();
int age();
@Memoized
default long longComputing() {
return 0L;
}
}
Here's the corresponding generated record that demonstrates defualt
method memoization:
import pl.com.labaj.autorecord.Memoized;
import pl.com.labaj.autorecord.Memoizer;
import javax.annotation.Nullable;
import javax.annotation.processing.Generated;
import static java.util.Objects.requireNonNull;
@Generated("pl.com.labaj.autorecord.AutoRecord")
record PersonRecord(String name, int age, @Nullable Memoizer<Long> longComputingMemoizer) implements Person {
PersonRecord {
requireNonNull(name, () -> "name must not be null");
}
PersonRecord(String name, int age) {
this(name, age, new Memoizer<>());
}
@Memoized
@Override
public long longComputing() {
return longComputingMemoizer.computeIfAbsent(() -> Person.super.longComputing());
}
}
An artificial longComputingMemoizer
component is introduced to keep memoized value of first original longComputing()
method execution.
To allow user not to use main constructor with it, additional constructor is generated with parameters recognized as components from the interface.