Java:通用过滤器映射函数

时间:2011-02-20 21:12:26

标签: java filter map clone

我正在尝试开发一个通用函数来过滤地图。

我到目前为止的代码是:

public static Map<?, ?> filterAttrs(Map<?, ?> args, String... unless) {

    Map<?, ?> filteredAttrs = Map.class.newInstance();

    Arrays.sort(unless);
    for (Object o : args.keySet()) {
        if (Arrays.binarySearch(unless, o.toString()) < 0 ) {
            filteredAttrs.put(o, args.get(o));
        }
    }
    return filteredAttrs;
}

我在filteredAttrs.put

中收到以下错误
  

在Map类型中放置(捕获#5-of?,捕获#6-of?)的方法不适用于参数(对象,捕获#8-of?)

我不知道如何实例化泛型Map(我试过1Map.class.newInstance()`)。

有什么想法吗?

编辑:在阅读了许多答案后,问题似乎是如何使filteredAttrs成为与args相同类型的实例。 (Map) args.getClass().newInstance()似乎可以解决问题。

5 个答案:

答案 0 :(得分:8)

此代码的问题在于类型系统阻止您将对象放入其键类型为Map的{​​{1}}中。这是因为如果密钥类型为?,则编译器不知道实际存储在地图中的内容 - 可以是?ObjectInteger - 因此无法确认您尝试添加到地图中的内容实际上是正确的类型,并且不会在List<Object>中不合适。例如,如果你有这个方法:

Map

然后编写如下代码:

public static void breakMyMap(Map<?, ?> m) {
    m.put(new Object(), new Object()); // Won't compile
}

然后,如果Map<String, String> myMap = new HashMap<String, String>(); breakMyMap(myMap); 中的代码要编译,它会将一对breakMyMap作为键和值放入Object,打破所有元素确实存在的不变量Map<String, String>秒。

要解决此问题,请更改函数,以便您拥有关于键和值的更多类型信息,而不是让此函数在String上运行。例如,你可以试试这个:

Map<?, ?>

既然编译器知道密钥类型为public static <K, V> Map<K, V> filterAttrs(Map<K, V> args, String... unless) { Map<K, V> filteredAttrs = new HashMap<K, V>(); Arrays.sort(unless); for (K o : args.keySet()) { String attr = o.toString(); if (Arrays.binarySearch(unless, o.toString()) < 0 ) { filteredAttrs.put(o, args.get(o)); } } return filteredAttrs; } ,它就可以验证K是否会混淆地图中密钥的类型。

我应该指出的另一件事是你所拥有的代码即使编译也不会有效。原因是该行

put

在运行时会导致异常,因为Map<?, ?> filteredAttrs = Map.class.newInstance(); 接口,而不是类,因此尝试使用Map创建它的实例将无法正常工作正确。要解决这个问题,您可以显式指定地图的类型(正如我在上面的代码中所做的那样),或者获取参数的类:

newInstance

当然,这也不能保证也能正常工作,尽管集合的一般合同是所有集合应该都有一个无参数的构造函数。

希望这有帮助!

答案 1 :(得分:5)

如果人们重新发明轮子(并没有做得更好),我认为这是不好的。

要在一个建议中修复所有问题:您可以尝试Google Guava吗?

例如,您可以使用Maps

  • Maps.filterEntries
  • Maps.filterKeys
  • Maps.filterValues

或者至少编写一个以谓词为参数的过滤器。

答案 2 :(得分:3)

这一行

Map<?, ?> filteredAttrs = Map.class.newInstance();

应抛出InstantiationException - 因为Map是一个接口 - 除非你已经实现了自己的名为Map的类

答案 3 :(得分:3)

你不能把任何东西放到Map<?, ?>中,因为编译器并不确切知道它是什么类型的地图。在该行:

filteredAttrs.put(o, args.get(o));

您假装Map<?, ?>Map<Object, Object>,但事实并非如此。 Angelika Langer的Java Generics FAQ更详细地解释了why this doesn't work

您应该为方法添加类型参数;那么也没有必要通过反射创建新地图:

public static <K, V> Map<K, V> filterAttrs(Map<K, V> args, String... unless) {
    Map<K, V> filteredAttrs = new HashMap<K, V>();

    Arrays.sort(unless);
    for (K o : args.keySet()) {
        if (Arrays.binarySearch(unless, o.toString()) < 0) {
            filteredAttrs.put(o, args.get(o));
        }
    }
    return filteredAttrs;
}

答案 4 :(得分:2)

您无法实例化Map,因为它是一个接口。不要使用?因为它会引起混淆。

public static <T, K> Map<T, K> filterAttrs(Map<T, K> args, T... unless) {
    Map<T, K> filteredAttrs;

    filteredAttrs = new HashMap<T, K>();
    Arrays.sort(unless);
    for (T o : args.keySet()) {
        if (Arrays.binarySearch(unless, o) < 0) {
            filteredAttrs.put(o, args.get(o));
        }
    }

    return filteredAttrs;
}

你有这条线什么都不做,所以我把它删除了: -

String attr = o.toString();