Collector.toMap using generics

Viewed 411

I am having hard time using streaming API in Java for generics map. I have a map which extends LinkedHashMap in the following way

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
  private int size;

  public LRUCache(int size) {
    super(size);
    this.size = size;
  }

  @Override
  protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
    return size() > size;
  }

  public LRUCache<K, V> collect() {
    return entrySet()
        .stream()
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
    );
  }

}

I am experimenting with a dummy method collect which will actually stream on the entrySet, apply some filters on it and then return a new LRUCache, but Collectors.toMap keep throwing an error which says

"Non-static method cannot be referenced from a static context"

I know this is some issue with Collectors.toMap generics definition. But, I am not able to figure out the right generics to get rid of the error and achieve the streaming and collecting functionality

2 Answers

LinkedHashMap includes in its implementation is a no-argument constructor that acts as a supplier to the toMap collect operation. You can introduce the same as well by including:

public LRUCache() {
    this(10); // default size
}

Thereafter you can collect the LRUCache implementation using the toMap override with LRUCache::new supplier in the following manner:

public LRUCache<K, V> collect() {
    return entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey,
                    Map.Entry::getValue, 
                    (a, b) -> a, LRUCache::new));
}

Note:- That what matters is providing a supplier to collect to a different implementation than a HashMap which is what you get using the other overloaded implementation of toMap.

You appear simply to be trying to copy your map/cache. There is no need for streams to do this.

Add a (maybe private) constructor, which invokes the map copy constructor:

private LRUCache(Map<K, V> map, int size) {
  super(map);
  this.size = size;
}

Then just use this in your collect method:

public LRUCache<K, V> collect() {
  return new LRUCache<>(this, size);
}

Or, without adding the constructor:

public LRUCache<K, V> collect() {    
  LRUCache<K, V> copy = new LRUCache<>(size);
  copy.putAll(this);
  return copy;
}
Related