我希望这个问题对这个论坛来说不是太基本的,但我们会看到的。我想知道如何重构一些代码以获得更好的性能,这是运行了很多次。

假设我正在使用Map(可能是HashMap)创建一个词频列表,其中每个键都是一个包含要统计的单词的String,值是一个Integer,该Integer在每次找到单词的标记时递增。

在Perl中,增加这样一个值非常简单:

$map{$word}++;

但在Java中,这要复杂得多。下面是我目前的做法:

int count = map.containsKey(word) ? map.get(word) : 0;
map.put(word, count + 1);

这当然依赖于新Java版本中的自动装箱特性。我想知道您是否可以建议一种更有效的方法来增加这个值。是否有更好的性能理由避开Collections框架而使用其他框架呢?

更新:我已经对几个答案做了测试。见下文。


当前回答

与其调用containsKey(),不如直接调用map更快。获取并检查返回值是否为空。

    Integer count = map.get(word);
    if(count == null){
        count = 0;
    }
    map.put(word, count + 1);

其他回答

2016年一点研究:https://github.com/leventov/java-word-count,基准源代码

每种方法的最佳效果(越小越好):

                 time, ms
kolobokeCompile  18.8
koloboke         19.8
trove            20.8
fastutil         22.7
mutableInt       24.3
atomicInteger    25.3
eclipse          26.9
hashMap          28.0
hppc             33.6
hppcRt           36.5

时间、空间的结果:

可以使用Java 8提供的Map接口中的computeIfAbsent方法。

final Map<String,AtomicLong> map = new ConcurrentHashMap<>();
map.computeIfAbsent("A", k->new AtomicLong(0)).incrementAndGet();
map.computeIfAbsent("B", k->new AtomicLong(0)).incrementAndGet();
map.computeIfAbsent("A", k->new AtomicLong(0)).incrementAndGet(); //[A=2, B=1]

方法computeIfAbsent检查指定的键是否已经与某个值关联?如果没有关联值,则尝试使用给定的映射函数计算其值。在任何情况下,它都会返回与指定键关联的当前值(现有值或计算值),如果计算值为空则返回null。

另一方面,如果你遇到多个线程更新一个公共和的情况,你可以看看LongAdder类。在高争用情况下,该类的预期吞吐量显著高于AtomicLong,但代价是更高的空间消耗。

在java 8中,简单易行的方法如下:

final ConcurrentMap<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();
    map.computeIfAbsent("foo", key -> new AtomicLong(0)).incrementAndGet();

各种原语包装器(例如Integer)都是不可变的,因此实际上没有更简洁的方法来实现您的要求,除非您可以使用AtomicLong之类的东西来实现。我可以在一分钟内进行更新。顺便说一下,哈希表是集合框架的一部分。

有几种方法:

使用袋子算法,比如谷歌集合中包含的集合。 创建可变容器,你可以在Map中使用:


    class My{
        String word;
        int count;
    }

并使用put(“word”,new My(“word”));然后您可以检查它是否存在,并在添加时增加。

避免使用列表来滚动您自己的解决方案,因为如果您使用内循环搜索和排序,您的性能将会非常糟糕。第一个HashMap解决方案实际上相当快,但在谷歌Collections中找到的合适的解决方案可能更好。

使用谷歌集合计数单词,看起来像这样:



    HashMultiset s = new HashMultiset();
    s.add("word");
    s.add("word");
    System.out.println(""+s.count("word") );


使用HashMultiset是非常优雅的,因为在计数单词时,袋算法正是您所需要的。