ExpiringMap Documentation

repository·master·Indexed 21 days ago

https://github.com/jhalterman/expiringmap

A high-performance, thread-safe ConcurrentMap implementation that automatically expires entries based on time or size constraints. Features include configurable expiration policies (CREATED or ACCESSED), variable expiration for individual entries, synchronous and asynchronous expiration listeners, and lazy entry loading via EntryLoader.

Tokens
1.8K
Snippets
7
Records
7
Agent score
27%

What's inside ExpiringMap

  1. Configure Expiration Policies

    master

    You can control whether an entry expires based on when it was first created or when it was last accessed.

    • ExpirationPolicy.CREATED: The entry expires after the specified duration from its creation time.
    • ExpirationPolicy.ACCESSED: The entry expires after the specified duration from its last access (e.g., via get()).

    You can set a global policy via the builder or override it for specific entries and keys.

    // Set global policy
    Map<String, Connection> map = ExpiringMap.builder()
      .expirationPolicy(ExpirationPolicy.ACCESSED)
      .build(); 
    
    // Set policy for a specific entry during put
    map.put("connection", connection, ExpirationPolicy.CREATED);
    
    // Change policy for an existing key on the fly
    map.setExpirationPolicy("connection", ExpirationPolicy.ACCESSED);
  2. Use Variable Expiration for individual entries

    master

    By default, ExpiringMap uses a constant expiration time for all entries. If you enable .variableExpiration() in the builder, you can specify unique expiration times and policies for every entry.

    Note on Performance: When variable expiration is enabled, put and remove operations have a time complexity of O(log n). When disabled (default), they are O(1).

    ExpiringMap<String, Connection> map = ExpiringMap.builder()
      .variableExpiration()
      .build();
    
    // Put entry with specific policy and duration
    map.put("connection", connection, ExpirationPolicy.ACCESSED, 5, TimeUnit.MINUTES);
    
    // Update expiration/policy for an existing key
    map.setExpiration(connection, 5, TimeUnit.MINUTES);
    map.setExpirationPolicy(connection, ExpirationPolicy.ACCESSED);
  3. Create an ExpiringMap with basic configuration

    master

    Use ExpiringMap.builder() to create a thread-safe ConcurrentMap that expires entries based on a fixed time duration or a maximum size. This is useful for managing caches or connection pools where you want to prevent memory leaks by automatically removing old data.

    Map<String, Connection> map = ExpiringMap.builder()
      .maxSize(123)
      .expiration(30, TimeUnit.SECONDS)
      .build();
      
    // Expires after 30 seconds or as soon as a 124th element is added
    map.put("connection", connection);
  4. Configure ThreadFactory for Google App Engine

    master

    If running on Google App Engine (GAE), you must set a custom ThreadFactory before creating the map to avoid runtime permission errors. Use the GAE ThreadManager to provide the factory.

    ExpiringMap.setThreadFactory(com.google.appengine.api.ThreadManager.currentRequestThreadFactory());
    ExpiringMap.create();
  5. Introspect Expiration and Reset Timers

    master

    The ExpiringMap API provides methods to inspect when an entry is scheduled to expire or to manually reset its timer.

    // Get the timestamp when the entry is expected to expire
    long expiration = map.getExpectedExpiration("jodah.net");
    
    // Reset the internal expiration timer for a key
    map.resetExpiration("jodah.net");
    
    // Get the configured expiration duration for a key
    long expiration = map.getExpiration("jodah.net");
  6. Implement Expiration Listeners

    master

    Listeners allow you to perform cleanup actions (like closing a database connection) when an entry expires.

    • expirationListener: Synchronous. Write operations to the map are blocked until the listener completes.
    • asyncExpirationListener: Asynchronous. Listeners are called in a separate thread pool and do not block map operations.

    Listeners can be added or removed dynamically using addExpirationListener and removeExpirationListener.

    // Synchronous listener
    Map<String, Connection> map = ExpiringMap.builder()
      .expirationListener((key, connection) -> connection.close())
      .build();
    
    // Asynchronous listener
    Map<String, Connection> map = ExpiringMap.builder()
      .asyncExpirationListener((key, connection) -> connection.close())
      .build();
    
    // Dynamic management
    ExpirationListener<String, Connection> connectionCloser = (key, connection) -> connection.close();
    map.addExpirationListener(connectionCloser);
    map.removeExpirationListener(connectionCloser);
  7. Use Lazy Entry Loading with EntryLoader

    master

    You can configure the map to automatically load values when get(key) is called for a missing key using an EntryLoader.

    • Use .entryLoader(Function) to provide a standard loader.
    • Use .expiringEntry(Function) to provide a loader that returns an object containing both the value and its specific expiration settings.
    // Standard lazy loading
    Map<String, Connection> connections = ExpiringMap.builder()
      .expiration(10, TimeUnit.MINUTES)
      .entryLoader(address -> new Connection(address))
      .build();
      
    connections.get("jodah.net"); // Loads connection via EntryLoader
    
    // Lazy loading with variable expiration per entry
    Map<String, Connection> connections = ExpiringMap.builder()
      .expiringEntry(address -> new ExpiringValue(new Connection(address), 5, TimeUnit.MINUTES))
      .build();