1

I am attempting to filter down a map to a sub set of itself. To achieve this I have attempted to implement the following utils method:

/**
* Reduces the size of the map. Starts from the first entry in the map.
* @param map The map.
* @param size The size to reduce the map by.
* @return The reduced size map.
*/
public static <T> Map<T, T> subList(@NonNull Map<T, T> map, int size) {
  val sub = new HashMap<T, T>();
  for (Map.Entry<T, T> entry : map.entrySet()) {
      if (sub.size() > size) {
          break;
      }

      sub.put(entry.getKey(), entry.getValue());
  }
  return sub;
}

I am then attempting to use it like this:

@Override
public Set<FileTransfer> upload(@NonNull ConnectionConfiguration target, @NonNull Map<FileTransfer, File> fileExports) {
    val batchSize = getBatchSize();
    if (fileExports.size() > batchSize) {
        fileExports = (Map<FileTransfer, File>) MapUtils.subList(fileExports, batchSize);
    }

    ...
}

But I am getting the error:

reason: Incompatible equality constraint: FileTransfer and File

I had assumed that I could pass a Map<FileTransfer, File> to the method because both types extend Object. What am I doing wrong here?

crmepham
  • 4,676
  • 19
  • 80
  • 155
  • 1
    Your method expects a `Map` where the `type` of the keys and values are the same (like for example `Map`. You should change the signature to something like `public static Map subList(@NonNull Map map, int size)` – name not found Apr 10 '20 at 09:21
  • In Java, [generics are invariant](https://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-are-java-generics-not-implicitly-po). – MC Emperor Apr 10 '20 at 09:27

1 Answers1

2

subList accepts a Map<T, T>. Note that the two generic parameters are the same, so you can only pass things like Map<Integer, Integer>, Map<String, String> or Map<FileTransfer, FileTransfer>. You are trying to pass Map<FileTransfer, File>, which has different generic parameters.

You should rewrite the subList method so that it accepts a Map<K, V>. Note now that the generic parameters are different, so maps with different key type and value type can be passed in.

public static <K, V> Map<K, V> subList(@NonNull Map<K, V> map, int size) {
  val sub = new HashMap<K, V>();
  for (Map.Entry<K, V> entry : map.entrySet()) {
      if (sub.size() > size) {
          break;
      }

      sub.put(entry.getKey(), entry.getValue());
  }
  return sub;
}

Now you don't even need the cast at the caller's side:

fileExports = MapUtils.subList(fileExports, batchSize);
Sweeper
  • 213,210
  • 22
  • 193
  • 313