有效地查看(或复制)Kotlin中大型HashMap的子集

时间:2018-12-13 19:09:44

标签: kotlin hashmap

我试图从一个巨大的哈希图中创建一个子哈希图,而不复制原始哈希图。

当前我使用这个:

i

这花费了我目前算法的50%左右。因为java确实经常计算val map = hashMapOf<Job, Int>() val copy = HashMap(map) listToRemoveFromCopy.forEach { copy.remove(it) } 的哈希值。 我只希望在新变量中将job减去map,而不要从原始列表中删除listToRemoveFromCopy元素。

有人知道吗?

感谢帮助

3 个答案:

答案 0 :(得分:1)

首先,您需要缓存Job的哈希码,因为如果无法拥有Job对象的集合或映射,则使用的任何方法都将效率低下以最快的速度。

希望,使它成为哈希码的部分是不可变的,否则不应将其用作键。在地图或集合中使用键时,突变键非常危险。您应该在第一次调用hashCode()时将其缓存,以免在此之前不会产生费用,除非您确定始终需要它。

然后将listToRemoveFromCopy更改为Set,以便可以多种方式有效地使用它。您需要先执行之前的步骤。

现在您有多个选择。最有效的是:

Guava具有实用程序功能Maps.filterKeys,可将视图返回到地图中,并且您可以创建一个谓词,该谓词与要删除的项Set相对。

 val removeKeys = listToRemoveFromCopy.toSet()
 val mapView = Maps.filterKeys(map, Predicates.not(Predicates.in(removeKeys)))

但是请务必注意视图上的某些方法不是很有效。如果您避免使用这些方法,这将是效果最好的选项:

  

许多过滤后的地图方法(例如size())会遍历基础地图中的每个键/值映射,并确定哪个满足过滤条件。如果不需要实时取景,则复制过滤后的地图并使用副本可能会更快。

如果您需要制作副本,则可以采用以下几种方法:

在地图上使用filterKeys一次即可创建一个新地图。如果删除列表可能在总键中占较大比例,则这很好。

val removeKeys = listToRemoveFromCopy.toSet()
val newMap = map.filterKeys { it !in removeKeys }

您应该注意的另一个诱人的选项是减号-,该运算符将复制整个地图,然后将其删除。它可以按原样使用listToRemoveFromCopy,而不必将其作为一个集合,但是完整的地图副本可能会抵消其好处。因此,除非删除列表中只有一小部分键,否则不要这样做。

val newMapButSlower = map - listToRemoveFromCopy

您可以根据地图大小与删除列表大小之间的比率来选择一个模型,而不是另一个模型,找到一个适合您“巨大”的断点。

可以在地图中添加自己的视图以避免复制,但并非无关紧要(因此,我的意思是非常复杂)。您重写的每个方法都必须始终执行正确的操作(包括地图自己的hashCodeequals),并且必须围绕键集和值创建其他视图。 entrySet很讨厌正确。在尝试您自己的(上面的番石榴或其他番石榴)之前,我会寻找一种预先编写的解决方案。零拷贝模型将是最有效的解决方案,但是代码最多,如果“巨大”意味着需要大量处理时间,我将在相同情况下执行该操作。如果您误解了实施合同的任何部分,那么使用这种方法会有很多错误。

您可以使用一种在操作项目时保持size属性的Guava解决方案来包装它,因此对于这种情况是有效的。如果您知道原始地图是只读的,则还可以编写一个更有效的解决方案。有关想法,请查看FilteredKeyMap的Guava实现及其祖先AbstractFilteredMap

总之,您的哈希码缓存可能会为您带来最大的努力成果。从那里开始。您甚至需要使用番石榴方法。

答案 1 :(得分:0)

您可以使用filterKeys功能。只会迭代地图一次

val copy = map.filterKeys { it !in listToRemoveFromCopy }

答案 2 :(得分:0)

除了Axel的直接答案:

可以优化计算作业的哈希码吗?如果无法加快计算速度,是否可以缓存结果? (对此有很多先例,包括java.lang.String。)或者,如果该类不在您的控制之下,您是否可以创建一个覆盖哈希码计算的委托/包装程序?