java.util.Map values()方法性能

时间:2018-05-05 15:07:29

标签: java dictionary collections hashmap

我有一张这样的地图,有数百万条:

private final Map<String, SomeItem> tops = new HashMap<>();

我需要获取值列表,可以通过调用java.util.Map values()方法来完成。

问题

每次调用此方法时都会创建值集合,或者它是预先计算出来的,并且从性能角度保存它以多次调用它?

问题在于,在我的情况下,Map有数百万个元素,每次调用values()时我都不想创建新列表

4 个答案:

答案 0 :(得分:3)

以下是<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>demo</groupId> <artifactId>demo_batch</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo_batch</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> $my_var = file_get_contents('http://example.com/token.php'); echo $my_var; 的复制实施:

Map.values()

这清楚地表明除非必要,否则不会创建值集合。因此,调用java.util.HashMap

不应导致额外开销

答案 1 :(得分:1)

这里的一个重点可能是:没关系!

但首先,参考到目前为止的其他答案:在那里返回的集合通常是“缓存”,因为它是懒惰创建的,之后,将返回相同的实例。例如,考虑HashMap类中的实现:

public Collection<V> values() {
    Collection<V> vs;
    return (vs = values) == null ? (values = new Values()) : vs;
}

documentation of the AbstractMap class(大多数Map实现所依据的)甚至指定了(作为合同的一部分,作为实现规范):

  

首次调用此方法时会创建该集合,并在响应所有后续调用时返回该集合。没有执行同步,因此对此方法的多次调用很可能不会返回同一个集合。

但是现在,人们可能会争辩说实施可能会在以后发生变化。 HashMap类的实现可能会发生变化,或者可能会切换到不扩展Map的另一个AbstractMap实现,并且实现方式也不同。它是当前这样实现的事实(对于它自己)并不能保证总是像这样实现。

因此,更重要的一点(以及无关紧要的原因)是values()方法确实应该返回集合视图。如documentation of the Map interface中所述:

  

Map接口提供了三个集合视图,它们允许将地图的内容视为一组键,值集合或键值映射集。

具体而言,documentation of the Map#values() method

  

返回此地图中包含的值的Collection视图。该集合由地图支持,因此对地图的更改将反映在集合中,反之亦然。

我无法想象实现涉及处理Map的所有值的视图的合理方法。

例如,假设HashMap中的实现是这样的:

public Collection<V> values() {
    return new Values();
}

然后每次调用它时都会返回 new 集合。但是创建此集合确实涉及处理值。

或者这样说:调用此方法的成本是独立的大小。它基本上具有单个对象创建的成本,无论地图是否包含10或10000个元素。

答案 2 :(得分:0)

正如其他人所提到的,你可以通过查看代码来看到这一点。您还可以编写一个快速示例来向自己证明。下面的代码将打印为真10次,因为对象标识对于值始终是相同的。

public static void main(String[] args) {
    Map<String, String> myMap = new HashMap();
    Collection<String> lastValues = myMap.values();
    for (int i=0; i < 10; i++) {
        System.out.println(lastValues == myMap.values());
        lastValues = myMap.values();
    }
}

以下代码将在第一次打印为true,然后在接下来的9次打印为false。

public static void main(String[] args) {
    Map<String, String> myMap = new HashMap();
    Collection<String> lastValues = myMap.values();
    for (int i=0; i < 10; i++) {
        System.out.println(lastValues == myMap.values());
        lastValues = myMap.values();
        myMap = new HashMap();
    }
}

答案 3 :(得分:0)

阅读此主题后的另一个建议是,如果Map tops声明的内容未更改 - 您可以使用google guava ImmutableMap对象。有关详细信息,请访问UnmodifiableMap (Java Collections) vs ImmutableMap (Google)