2

我尝试使用文件内容构建地图,我的代码如下:

    System.out.println("begin to build the sns map....");
    String basePath = PropertyReader.getProp("oldbasepath");
    String pathname = basePath + "\\user_sns.txt";
    FileReader fr;
    Map<Integer, List<Integer>> snsMap = 
            new HashMap<Integer, List<Integer>>(2000000);
    try {
        fr = new FileReader(pathname);
        BufferedReader br = new BufferedReader(fr);
        String line; 
        int i = 1;
        while ((line = br.readLine()) != null) {
            System.out.println("line number: " + i);
            i++;

            String[] strs = line.split("\t");
            int key = Integer.parseInt(strs[0]);
            int value = Integer.parseInt(strs[1]);
            List<Integer> list = snsMap.get(key);
            //if the follower is not in the map
            if(snsMap.get(key) == null) 
                list = new LinkedList<Integer>();
            list.add(value);
            snsMap.put(key, list);
            System.out.println("map size: " + snsMap.size());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("finish building the sns map....");
    return snsMap;

该程序起初非常快,但当打印的信息是:

 map size: 1138338
 line number: 30923602
 map size: 1138338
 line number: 30923603 
 ....

我试图用两个 System.out.println() 子句来判断 BufferedReader 和 HashMap 而不是 Java 分析器的性能。有时获取行号信息后需要一段时间才能获取地图大小的信息,有时获取地图大小后需要一段时间才能获取行号信息的信息。我的问题是:这让我的程序变慢了?大文件的 BufferedReader 或大地图的 HashMap?

4

5 回答 5

3

如果您从 Eclipse 内部对此进行测试,您应该意识到写入 stdout/stderr 的巨大性能损失,因为 Eclipse 在控制台视图中捕获该输出。在紧密循环中打印始终是一个性能问题,即使在 Eclipse 之外也是如此。

但是,如果您抱怨的是处理 3000 万行后出现的速度下降,那么我敢打赌这是内存问题。首先,由于强烈的 GC'ing,它变慢了,然后它与OutOfMemoryError.

于 2012-05-17T11:10:26.553 回答
2

您将不得不使用一些分析工具检查您的程序,以了解为什么它很慢。一般来说,文件访问比内存操作慢得多(除非你在内存中受到限制并进行过多的 GC),所以猜测是读取文件在这里可能会更慢。

于 2012-05-17T11:07:40.627 回答
2

在进行分析之前,您将不知道什么是慢的,什么不是。

最有可能的是,System.out将显示为瓶颈,然后您将不得不再次在没有它们的情况下进行分析。System.out是寻找性能瓶颈的最糟糕的事情,因为这样做通常会增加更严重的瓶颈。

您的代码的一个明显优化是移动该行

snsMap.put(key, list);

进入if 声明。您只需要在创建列表时放置它。否则,看跌期权只会用自己替换当前值。

Integer对象相关的 Java 成本(尤其是 Java Collections API 中整数的使用)主要是内存(以及垃圾收集!)问题。有时,您可以通过使用GNU trove等原始集合获得显着收益,这取决于您可以如何调整代码以有效地使用它们。Trove 的大部分优势在于内存使用。绝对尝试重写您的代码以使用TIntArrayListTIntObjectMap来自 GNU trove。我也会避免使用链表,尤其是对于原始类型。

粗略估计,aHashMap<Integer, List<Integer>>每个条目至少需要 3*16 字节。双向链表再次需要每个存储条目至少 2*16 字节。1m 个键 + 30m 个值 ~ 1 GB。还没有包括开销。使用 GNU trove TIntObjectHash<TIntArrayList>,每个键应该是 4+4+16 个字节,每个值应该是 4 个字节,所以 144 MB。两者的开销可能相似。

Trove 使用较少内存的原因是这些类型专门用于原始值,例如int. 它们将int直接存储值,因此每个使用 4 个字节来存储。

Java 集合HashMap由许多对象组成。它大致看起来像这样:有一些Entry对象分别指向一个键和一个值对象。这些必须是对象,因为泛型在 Java 中的处理方式。在您的情况下,键将是一个Integer对象,它使用 16 个字节(4 个字节标记,4 个字节类型,4 个字节实际int值,4 个字节填充)AFAIK。这些都是 32 位系统估计。因此,单个条目HashMap可能需要大约 16(条目)+ 16(整数键)+ 32(但为空的 LinkedList)字节的内存,所有这些都需要考虑用于垃圾回收。

如果你有很多Integer对象,它只需要4 倍的内存,就好像你可以使用int原语存储所有东西一样。这是您为在 Java 中实现的干净 OOP 原则所付出的代价。

于 2012-05-17T11:32:45.373 回答
0

最好的方法是使用分析器(例如,JProfile)运行您的程序并查看哪些部分运行缓慢。例如,调试输出也会减慢您的程序。

于 2012-05-17T11:07:40.863 回答
0

Hash Map 并不慢,但实际上它是地图中最快的。HashTable 是地图中唯一的线程安全的,有时可能很慢。

重要提示:在您读取数据后关闭 BufferedReader 和 File ...这可能会有所帮助。

例如:br.close() file.close()

请从任务管理器中检查您的系统进程,可能有太多进程在后台运行。

有时 Eclipse 是真正的资源重,所以尝试从控制台运行它来检查它。

于 2012-05-17T11:11:48.927 回答