or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

classfile-builder.mdclassfile.mdindex.mdmemoize.mdpreview.mdresult.mdsignatures.mdstream.mdunmodifiable.md

memoize.mddocs/

0

# Memoization Utilities

1

2

Thread-safe memoization utilities for caching expensive computations with configurable eviction policies and resource management. This package provides lazy evaluation with automatic caching to improve performance of repeated operations.

3

4

## Capabilities

5

6

### Basic Memoization

7

8

Thread-safe memoization for expensive computations with lazy evaluation and automatic caching.

9

10

```java { .api }

11

/**

12

* Thread-safe memoization supplier with lazy evaluation

13

* @param <S> Type of the cached value

14

*/

15

public interface Memoize<S> extends Supplier<S> {

16

/**

17

* Create a memoizing supplier that caches the result of the given supplier

18

* @param supplier The supplier to memoize

19

* @return Memoize instance that caches the supplier's result

20

*/

21

static <T> Memoize<T> supplier(Supplier<? extends T> supplier);

22

23

/**

24

* Create a memoizing function application with a fixed argument

25

* @param function The function to memoize

26

* @param argument The fixed argument to apply

27

* @return Memoize instance that caches the function result

28

*/

29

static <T, R> Memoize<R> supplier(Function<? super T, ? extends R> function, T argument);

30

31

/**

32

* Get the cached value, computing it if necessary

33

* This method is thread-safe and will only compute the value once

34

* @return The cached value

35

*/

36

S get();

37

38

/**

39

* Peek at the cached value without triggering computation

40

* @return The cached value, or null if not yet computed

41

*/

42

S peek();

43

44

/**

45

* Check if the value has been computed and cached

46

* @return true if the value is cached, false if it would need to be computed

47

*/

48

boolean isPresent();

49

50

/**

51

* Transform the cached value using the given function

52

* The transformation is itself memoized

53

* @param mapper Function to transform the cached value

54

* @return new Memoize instance with the transformed value

55

*/

56

<R> Memoize<R> map(Function<? super S, ? extends R> mapper);

57

58

/**

59

* FlatMap operation for chaining memoized computations

60

* @param mapper Function that returns another Supplier

61

* @return new Memoize instance from the flattened result

62

*/

63

<R> Memoize<R> flatMap(Function<? super S, ? extends Supplier<? extends R>> mapper);

64

65

/**

66

* Filter the cached value with a predicate

67

* @param predicate Test to apply to the cached value

68

* @return Memoize that returns the value only if predicate passes, null otherwise

69

*/

70

Memoize<S> filter(Predicate<? super S> predicate);

71

72

/**

73

* Consume the cached value if present

74

* @param consumer Consumer to apply to the cached value

75

* @return this instance for method chaining

76

*/

77

Memoize<S> accept(Consumer<? super S> consumer);

78

79

/**

80

* Execute a consumer on the cached value if it's present

81

* @param consumer Consumer to execute

82

* @return this instance for method chaining

83

*/

84

Memoize<S> ifPresent(Consumer<? super S> consumer);

85

}

86

```

87

88

**Usage Example:**

89

90

```java

91

import aQute.bnd.memoize.Memoize;

92

import java.util.concurrent.TimeUnit;

93

94

// Create a memoized expensive computation

95

Memoize<String> expensiveComputation = Memoize.supplier(() -> {

96

System.out.println("Computing expensive result...");

97

// Simulate expensive work

98

try {

99

Thread.sleep(1000);

100

} catch (InterruptedException e) {

101

Thread.currentThread().interrupt();

102

throw new RuntimeException(e);

103

}

104

return "Expensive Result";

105

});

106

107

// First call: computes and caches

108

String result1 = expensiveComputation.get(); // Prints "Computing..." and returns result

109

System.out.println(result1);

110

111

// Second call: returns cached result immediately

112

String result2 = expensiveComputation.get(); // No computation, immediate return

113

System.out.println(result2);

114

115

// Check if computed without triggering computation

116

if (expensiveComputation.isPresent()) {

117

System.out.println("Value is cached");

118

}

119

120

// Peek at cached value

121

expensiveComputation.peek().ifPresent(System.out::println);

122

```

123

124

### Refreshing Memoization

125

126

Memoization with automatic cache expiration after a specified time period.

127

128

```java { .api }

129

/**

130

* Create a refreshing memoizing supplier that expires cached values after a time period

131

* @param supplier The supplier to memoize

132

* @param time_to_live Duration after which cached value expires

133

* @param unit Time unit for the duration

134

* @return Memoize instance that refreshes after the specified time

135

*/

136

static <T> Memoize<T> refreshingSupplier(Supplier<? extends T> supplier, long time_to_live, TimeUnit unit);

137

```

138

139

**Usage Example:**

140

141

```java

142

// Cache that refreshes every 5 minutes

143

Memoize<String> refreshingCache = Memoize.refreshingSupplier(

144

() -> fetchDataFromAPI(),

145

5, TimeUnit.MINUTES

146

);

147

148

// First call within 5 minutes - computes and caches

149

String data1 = refreshingCache.get();

150

151

// Call within 5 minutes - returns cached data

152

String data2 = refreshingCache.get(); // Same as data1

153

154

// After 5 minutes - recomputes and caches new result

155

Thread.sleep(TimeUnit.MINUTES.toMillis(6));

156

String data3 = refreshingCache.get(); // Fresh computation

157

```

158

159

### Reference-based Memoization

160

161

Memoization using weak or soft references for automatic garbage collection of cached values.

162

163

```java { .api }

164

/**

165

* Create a reference-based memoizing supplier using the specified reference type

166

* @param supplier The supplier to memoize

167

* @param referenceFunction Function to create the reference type (WeakReference, SoftReference, etc.)

168

* @return Memoize instance using reference-based caching

169

*/

170

static <T> Memoize<T> referenceSupplier(

171

Supplier<? extends T> supplier,

172

Function<? super T, ? extends Reference<? extends T>> referenceFunction

173

);

174

```

175

176

**Usage Example:**

177

178

```java

179

import java.lang.ref.WeakReference;

180

import java.lang.ref.SoftReference;

181

182

// Weak reference memoization - value can be GC'd if no strong references exist

183

Memoize<LargeObject> weakMemoize = Memoize.referenceSupplier(

184

() -> new LargeObject(),

185

WeakReference::new

186

);

187

188

// Soft reference memoization - value survives until memory pressure

189

Memoize<LargeObject> softMemoize = Memoize.referenceSupplier(

190

() -> new LargeObject(),

191

SoftReference::new

192

);

193

194

LargeObject obj = softMemoize.get(); // Creates and caches

195

// obj reference keeps it alive even with weak reference

196

LargeObject obj2 = softMemoize.get(); // Returns same cached instance

197

```

198

199

### Predicate-based Memoization

200

201

Memoization that recomputes values when a predicate condition is not met.

202

203

```java { .api }

204

/**

205

* Create a predicate-based memoizing supplier that recomputes when predicate fails

206

* @param supplier The supplier to memoize

207

* @param predicate Predicate to test cached values; if false, value is recomputed

208

* @return Memoize instance that validates cached values with the predicate

209

*/

210

static <T> Memoize<T> predicateSupplier(Supplier<? extends T> supplier, Predicate<? super T> predicate);

211

```

212

213

**Usage Example:**

214

215

```java

216

// Cache that recomputes if cached value is null or empty

217

Memoize<String> validatingCache = Memoize.predicateSupplier(

218

() -> fetchConfigValue(),

219

value -> value != null && !value.isEmpty()

220

);

221

222

String config = validatingCache.get(); // Computes initially

223

String config2 = validatingCache.get(); // Returns cached if valid

224

225

// If fetchConfigValue() later returns null/empty, it will recompute

226

```

227

228

### Closeable Memoization

229

230

Specialized memoization for AutoCloseable resources with proper resource management.

231

232

```java { .api }

233

/**

234

* Memoization for AutoCloseable resources with automatic resource management

235

* @param <S> Type of the cached resource (must extend AutoCloseable)

236

*/

237

public interface CloseableMemoize<S extends AutoCloseable> extends Memoize<S>, AutoCloseable {

238

/**

239

* Create a memoizing supplier for closeable resources

240

* @param supplier Supplier that creates AutoCloseable resources

241

* @return CloseableMemoize instance that manages resource lifecycle

242

*/

243

static <T extends AutoCloseable> CloseableMemoize<T> closeableSupplier(Supplier<? extends T> supplier);

244

245

/**

246

* Check if the cached resource has been closed

247

* @return true if closed, false if open or not yet created

248

*/

249

boolean isClosed();

250

251

/**

252

* Get the cached resource, creating it if necessary

253

* @return The cached AutoCloseable resource

254

* @throws IllegalStateException if this CloseableMemoize has been closed

255

*/

256

S get();

257

258

/**

259

* Consume the cached resource if it exists and is not closed

260

* @param consumer Consumer to apply to the resource

261

*/

262

void accept(Consumer<? super S> consumer);

263

264

/**

265

* Execute a consumer on the cached resource if it exists and is not closed

266

* @param consumer Consumer to execute

267

*/

268

void ifPresent(Consumer<? super S> consumer);

269

270

/**

271

* Close the cached resource if it exists

272

* Subsequent calls to get() will throw IllegalStateException

273

*/

274

void close();

275

}

276

```

277

278

**Usage Example:**

279

280

```java

281

import aQute.bnd.memoize.CloseableMemoize;

282

import java.io.FileInputStream;

283

import java.io.IOException;

284

285

// Memoize a file input stream

286

CloseableMemoize<FileInputStream> fileStreamMemoize = CloseableMemoize.closeableSupplier(

287

() -> {

288

try {

289

return new FileInputStream("config.properties");

290

} catch (IOException e) {

291

throw new RuntimeException(e);

292

}

293

}

294

);

295

296

try (CloseableMemoize<FileInputStream> resource = fileStreamMemoize) {

297

// First access - creates and caches the FileInputStream

298

FileInputStream stream1 = resource.get();

299

// Process stream...

300

301

// Second access - returns the same cached FileInputStream

302

FileInputStream stream2 = resource.get(); // Same instance as stream1

303

304

// Use resource if present

305

resource.ifPresent(stream -> {

306

// Process the stream safely, only if it exists and is not closed

307

});

308

309

} // Automatically closes the cached FileInputStream

310

311

// Subsequent calls will fail

312

try {

313

fileStreamMemoize.get(); // Throws IllegalStateException

314

} catch (IllegalStateException e) {

315

System.out.println("Resource has been closed");

316

}

317

```

318

319

### Functional Operations

320

321

Chain and transform memoized values using functional programming patterns.

322

323

```java { .api }

324

// Map transformations

325

Memoize<String> stringSupplier = Memoize.supplier(() -> "hello");

326

Memoize<String> upperCase = stringSupplier.map(String::toUpperCase);

327

Memoize<Integer> length = stringSupplier.map(String::length);

328

329

// Filter operations

330

Memoize<String> nonEmptyString = Memoize.supplier(() -> getStringValue())

331

.filter(s -> !s.isEmpty());

332

333

Optional<String> value = nonEmptyString.get(); // Optional.empty() if string is empty

334

335

// FlatMap for chaining computations

336

Memoize<String> config = Memoize.supplier(() -> loadConfig());

337

Memoize<Database> database = config.flatMap(cfg -> Memoize.supplier(() -> connectToDatabase(cfg)));

338

```

339

340

## Thread Safety

341

342

All Memoize implementations are thread-safe:

343

344

- Multiple threads can safely call `get()` concurrently

345

- Computation happens exactly once even under concurrent access

346

- Memory visibility guarantees ensure all threads see the cached result

347

- No external synchronization is required

348

349

## Performance Characteristics

350

351

- **First Access**: Full computation cost plus small memoization overhead

352

- **Subsequent Accesses**: Near-zero cost (simple field access)

353

- **Memory Overhead**: One additional object per memoized supplier

354

- **Thread Contention**: Minimal - uses efficient double-checked locking pattern

355

356

## Best Practices

357

358

1. **Use for Expensive Operations**: Memoize computations that are costly in time or resources

359

2. **Consider Memory Usage**: Cached values remain in memory until the Memoize instance is GC'd

360

3. **Use CloseableMemoize**: For resources that need cleanup

361

4. **Reference-based Caching**: For large objects that should be GC-eligible

362

5. **Refreshing Caches**: For data that becomes stale over time