将HashSet <string>与String.equals(...)</string>进行比较

时间:2011-06-15 20:23:36

标签: java string optimization

如果我有一个String的设定数量,我想在自由格式字段中检查(计算机生成,也可能是每秒很多),这将是一个更快的实现?

private static HashSet<String> values = new HashSet<String>();
static {
   ... add 5 Strings to the Set
}
public void someMethod() {
   if (values.contains(enteredValue))
   ...
}

或使用5 String.equals ||进行if?

这对我来说似乎不费吹灰之力,但也许我错了。一个而不是另一个的任何缺点?

8 个答案:

答案 0 :(得分:5)

我相信HashSet会更快,因为它会将您的字符串哈希一次,然后进行5次整数比较。这比进行5次String比较要快得多。

话虽如此,我建议你只选择一种方法然后尝试一下。如果它的执行速度不够快,那就担心更多地优化它。

答案 1 :(得分:2)

字符串source code

哈希相关代码:

/** Cache the hash code for the string */
private int hash; // Default to 0

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31 * h + val[off++];
        }
        hash = h;
    }
    return h;
}

等于代码:

public boolean equals(Object anObject) {
    if (this  == anObject) {
        return true;
    }
    if (anObject instanceof  String) {
        String anotherString = (String) anObject;
        int n = count;
        if (n == anotherString.count) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = offset;
            int j = anotherString.offset;
            while (n-- != 0) {
                if (v1[i++] != v2[j++])
                    return false;
            }
            return true;
        }
    }
    return false;
}

因此,每个字符串都涉及字符串中所有字符的单个循环,哈希值只为每个字符串计算一次,但与哈希计算循环不同,等号循环在第一个字符不匹配时抢先退出,并且而且,如果字符串具有不同的长度,则甚至不会发生等于循环。

我的直觉是,除非你一遍又一遍地将相同的字符串与相同的字符串进行比较,否则等于获胜。

艰难的电话。如果您真的想知道哪个更适合您的应用程序,请做一个基准测试。

答案 2 :(得分:1)

只有一种方法可以确定 - 用现实值来衡量它。

答案 3 :(得分:1)

这取决于字符串的长度,内容和数量。

如果字符串很少且随机填充,那么简单比较很可能会在一个或两个字符内找到不匹配,只有当内容完全匹配时才进一步检查。与HashSet维护和哈希码生成(每次都是完整字符串)的开销相比,我打赌简单比较。

如果字符串可能相似或更多,HashSet会更好。

[请注意,假设HashSet的答案会更快,忽略您必须为HashSet的每次添加生成哈希码,而不仅仅是查找。但是,如果你的参考字符串不随时间改变,那么这个事实并不重要。]

答案 4 :(得分:0)

来自http://en.wikipedia.org/wiki/Java_hashCode%28%29#The_java.lang.String_hash_function

  

从Java 1.2开始,java.lang.String类   使用a实现其hashCode()   整个产品和算法   字符串的文字。

在这里疯狂猜测,但我不认为会有太大的区别,因为计算哈希本身与进行直接字符串比较的成本大致相同,而且你可能不得不处理冲突。

答案 5 :(得分:0)

HashSet不一定会更快,但时间将常量。引用Java文档。

  

这门课提供恒定的时间   基本操作的性能   (添加,删除,包含和大小)

因此,如果您添加更多字符串以搜索该值,如果您使用等于时间将相对于字符串 n 的数字但是HashSet它将保留恒定。

答案 6 :(得分:0)

如果您对字符串进行排序并进行二进制搜索,那么您最多将进行三次compareTo次测试。如果使用HashSet,则必须计算测试字符串的哈希值,并至少进行一次equals测试(如果匹配哈希码)或不进行equals测试(针对未命中)。我不清楚这里是否会有很大的不同,实际的性能可能取决于次优问题,如优化水平。

答案就像对待这类问题一样,是基准。

答案 7 :(得分:0)

在发现 string.equals 速度更快后来到这里寻找答案 - 并且与我的预期相反。但这是代码和结果。请反馈...

这是以纳秒为单位的结果:

  • mapSetContains 166000
  • stringCompare 143000
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;

public class Main {

    HashMap<Integer, String> testKeys = new HashMap<>();
    HashSet<String> keys2 = new HashSet<>();
    String key1 = null;
    String key2 = null;

    static HashMap<String, Long> results = new HashMap<String, Long>();

    public static void main(String[] args) {

        var test1 = new Main();
        var test2 = new Main();

        test1.init();
        test2.init();

        Instant start, finish;
        long timeElapsed;

        for (int i = 0; i < 10; i++) {

            start = Instant.now();
            test1.stringCompare();
            finish = Instant.now();
            timeElapsed = getElapsed(start, finish);

            addResult("stringCompare", timeElapsed);
            System.out.println("test1.run1 stringCompare time elapsed: " + timeElapsed);

            start = Instant.now();
            test2.stringCompare();
            finish = Instant.now();
            timeElapsed = getElapsed(start, finish);
            addResult("stringCompare", timeElapsed);
            System.out.println("test2.run1 stringCompare time elapsed: " + timeElapsed);

            //

            start = Instant.now();
            test1.mapSetContains();
            finish = Instant.now();
            timeElapsed = getElapsed(start, finish);
            addResult("mapSetContains", timeElapsed);
            System.out.println("test2.run1 mapSetContains time elapsed: " + timeElapsed);

            start = Instant.now();
            test2.mapSetContains();
            finish = Instant.now();
            timeElapsed = getElapsed(start, finish);
            addResult("mapSetContains", timeElapsed);
            System.out.println("test2.run2 mapSetContains time elapsed: " + timeElapsed);
        }

        emitResults();

    }


    static void emitResults(){
        for (String item : results.keySet()) {
            System.out.println("item: " + item + "  " + results.get(item));
            
        }
    }

    static void addResult(String testType, Long result) {
        if (results.containsKey(testType))
            results.put(testType, results.get(testType) + result);
        else
            results.put(testType, result);
    }

    static long getElapsed(Instant start, Instant finish) {
        long timeElapsed;
        timeElapsed = Duration.between(start, finish).toNanos(); // .toMillis();
        return timeElapsed;
    }

    void stringCompare() {

        for (int i = 0; i < testKeys.size(); i++) {

            String testKey = testKeys.get(i);
            boolean rv = testKey.equals(key1) || testKey.equals(key2);
        }

    }

    void mapSetContains() {

        for (int i = 0; i < testKeys.size(); i++) {

            String testKey = testKeys.get(i);
            boolean rv = keys2.contains(testKey);
        }

    }

    void init() {

        for (int i = 0; i < 4; i++) {
            testKeys.put(i, makeUiid());
        }

        key1 = testKeys.get(0);
        key2 = testKeys.get(1);

        keys2.add(testKeys.get(2));
        keys2.add(testKeys.get(3));
    }

    String makeUiid() {
        return java.util.UUID.randomUUID().toString();
    }
}