对于我的一个应用程序,必须经常调用以下函数。这个功能占用了大量的CPU,因此我想知道你是否知道如何提高性能。
代码计算四个字符组合的出现次数。在测试期间,我发现地图中的条目数大约为100. 文本的长度在100到800的范围内。初始大小为200是猜测,代码似乎是比没有指定初始大小更快地运行。但它可能不是最佳值。
private Map<String, Integer> getTetagramCount(final String text) {
final Map<String, Integer> cipherTetagrams = new HashMap<String, Integer>(200);
for (int i = 0; i < text.length() - 4; i++) {
final String tet = text.substring(i, i + 4);
final Integer count = cipherTetagrams.get(tet);
if (count != null) {
cipherTetagrams.put(tet, count + 1);
} else {
cipherTetagrams.put(tet, 1);
}
}
return cipherTetagrams;
}
答案 0 :(得分:11)
答案 1 :(得分:7)
您可以尝试将prefix tree (trie)作为数据结构实现,特别是如果您知道字符的范围。最多可达4级,为您提供潜在的恒定(和更快的恒定)时间。与hashmap相比,它的执行方式实际上取决于您拥有的数据。
修改强>
或者,如果您知道字符的范围,您可以将它们填充到更快的数据类型中。
由于您知道所有字符都在A和Z或0和9之间,因此您可以将其压缩为6位:
public int index(String str, int startPos) {
return
((str.charAt(startPos+3) - '0') << 18) +
((str.charAt(startPos+2) - '0') << 12) +
((str.charAt(startPos+1) - '0') << 6) +
(str.charAt(startPos) - '0');
}
//...
int[] counts = new int[42*42*42*42];
final int max = text.length() - 4;
for ( int i = 0; i < max; i++ ) {
counts[index(text, i)]++;
}
修改:更新了上面的示例以涵盖A-Z, 0-9
。现在注意两件事:首先,你必须创建一个大数组,但你不需要每次都这样做(你必须每次都清除它!)。其次,这提供了对某个单词出现次数的快速查找,但是如果要迭代所有单词(比如查找实际出现的所有单词),则需要O(42^4)
次。
答案 2 :(得分:4)
嗯,一个可能的选择是从使用不可变包装器类型转换为可变类型:
public final class Counter
{
private int value;
public int getValue()
{
return value;
}
public void increment()
{
value++;
}
}
然后将您的代码更改为:
private Map<String, Counter> getTetagramCount(final String text) {
final Map<String, Counter> cipherTetagrams = new HashMap<String, Counter>(200);
// Micro-optimization (may well not help) - only take the
// length and subtract 4 once
int lastStart = text.length() - 4;
for (int i = 0; i < lastStart; i++) {
final String tet = text.substring(i, i + 4);
Counter counter = cipherTetagrams.get(tet);
if (counter == null) {
counter = new Counter();
cipherTetagrams.put(tet, counter);
}
counter.increment();
}
return cipherTetagrams;
}
通过这种方式,您只需“放置”与单词关联的值一次......之后就可以将其增加到位。
(如果您想使用内置类型,则可以使用AtomicInteger
代替Counter
。)
答案 3 :(得分:1)
除了Big-O优化(如果有的话),有一种非常简单的方法可以大大加速你的应用程序:使用一些东西而不是默认的Java API,当它涉及到非常慢时处理批次的数据。
替换:
Map<String, Counter>
使用Trove(这意味着你必须将Trove jar添加到你的项目中):
TObjectIntHashMap<String>
和
final Integer count = cipherTetagrams.get(tet);
使用:
final int count = cipherTetagrams.get(tet);
因为当你使用 lot 数据时,使用像Integer这样的包装器而不是原语(比如int),并使用默认的Java API是最可靠的方式来拍摄自己。
答案 4 :(得分:-1)
我甚至没有开始分析你的代码,我注意到这个方法不能在任何成员字段上运行,并且可以是静态的。静态方法总是比非静态方法执行得更好。我会在一分钟内寻找更多问题......
答案 5 :(得分:-2)
我不确定这是否会更快,但我有一种感觉。
private Map<String, Integer> getTetagramCount( final String text) {
final List<String> list = new ArrayList<String>();
for( int i =0; i < text.length() - 4; i++) {
list.add( text.substring( i, i+4);
}
Collections.sort( list);
Map<String, Integer> map = new HashMap<String, Integer>( list.size());
String last = null;
int count = 0;
for( String tetagram : list) {
if( tetagram != last && last != null) {
map.put( tetagram, count);
count = 0;
}
count++;
last = tetagram;
}
if( tetagram != null) {
map.put( tetagram, count);
}
return map;
}
完成后,根据您对地图的操作,最后可能不需要转换为地图。