将binarySearch与Comparator和regex一起使用

时间:2010-08-11 08:55:46

标签: java search comparator

我正在尝试编写一个搜索List<String>的快速搜索 我想使用binarySearch,而不是循环遍历列表并手动检查,但我不知道该怎么做。

旧方式:

for(String s : list) {
  if(s.startsWith("contact.")
     return true;
}

相反,我想要这样的事情:

Collections.sort(list);
Collections.binarySearch(list, FindContactComparator());

有人可以帮我写这个比较器吗?
有没有更好的方法来做这个而不是使用binarySearch?

5 个答案:

答案 0 :(得分:3)

这应该有效:

        Comparator<String> startsWithComparator = new Comparator<String>() {
            public int compare(String currentItem, String key) {
                if(currentItem.startsWith(key)) {
                    return 0;
                }
                return currentItem.compareTo(key);
            }
        };

int index = Collections.binarySearch(items, "contact.", startsWithComparator);

然而,排序和二进制搜索的效率低于单次迭代。

<强>附录:

虽然上面的答案可以帮到你,但这是另一种方式(灵感来自Scala,Google Collections):

List<String> items = Arrays.asList("one", "two", "three", "four", "five", "six");
int index = find(items, startsWithPredicate("th"));
System.out.println(index);


public static Predicate<String> startsWithPredicate(final String key) {
    return new Predicate<String>(){
        @Override
        public boolean apply(String item) {
            return item.startsWith(key); 
        }
    };
}

public static <T> int find(Collection<T> items, Predicate<T> predicate) {
    int index = 0;
    for(T item: items) {
        if(predicate.apply(item)) {
            return index;
        }
        index++;
    }
    return -1;
}

interface Predicate<T> {
    boolean apply(T item);
}

这里的问题是find()方法与你的'匹配'逻辑无关;它只是找到一个满足谓词的元素。所以你可以传递一个不同的谓词实现,例如。它可以检查'endsWith'到find()方法,它将返回以特定字符串结尾的找到的项目。此外,find()方法适用于任何类型的集合;它需要的是一个谓词,它将集合元素类型的元素转换为布尔值。围绕简单逻辑的这些多行代码也表明Java缺乏对第一类函数的支持。

答案 1 :(得分:1)

问题在于二元搜索永远不会回头。 我通过使用二进制搜索找到第一个匹配的元素,然后向后循环以找到该子字符串的第一个匹配项,然后是一个收集所有匹配元素的循环来解决这个问题。

答案 2 :(得分:1)

我认为现在这样做的方式实际上是从性能角度来看的最佳方式。排序本身可能比简单地遍历未排序列表更昂贵。但是要确保你必须运行一些测试(尽管由于JIT编译而不是那么容易)。

您正在寻找的标准是否始于'以'开头'?因为在你的问题中你正在谈论一个正则表达式。

如果您确实希望实现此功能,则至少应使用与搜索相同的Comparator进行排序。比较器本身可以非常简单。只需编写一个将符合您标准的所有内容放在不符合标准的所有内容之前。我的语法可能不完全正确,因为我有一段时间没有完成Java。

public class MyComparator<string> implements Comparator<string> {
    private string prefix;
    public MyComparator(string prefix) {
        this.prefix = prefix;
    }
    public int compare(string s0, string s1) {
        if (s0.startsWith(prefix) && s1.startsWith(prefix)) {
            return 0;
        }
        else if (s0.startsWith(prefix)) {
            return -1;
        }
        else if (s1.startsWith(prefix)) {
            return 1;
        }
        return 0;
    }
    public bool equals(object comp) {
        return true;
    }
}

答案 3 :(得分:1)

对列表本身进行排序比列表的线性扫描花费更多时间。 (基于比较的排序需要时间与 n(log n)成比例,其中 n 是列表的长度。)

即使列表在大多数时间内完全排序,排序算法也必须至少遍历列表才能检查这一点。

基本上,无论你如何实现排序算法,算法(即使在最好的情况下)必须至少查看所有元素。因此,线性搜索“concat”可能是你最好的选择。


更精细的解决方案是子类化包含字符串的列表,并维护“concat”的第一个出现的索引。

鉴于字符串是不可变的,您所要做的就是覆盖添加,删除等,并相应地更新索引。

答案 4 :(得分:1)

只是另一个比较器(带正则表达式):

Comparator<String> comparator = new Comparator<String>() {

    private final Pattern containsPattern = Pattern.compile(searchTerm,Pattern.CASE_INSENSITIVE);

    public int compare(String o1, String o2) {

        Matcher contains1 = containsPattern.matcher(o1);
        Matcher contains2 = containsPattern.matcher(o2);
        boolean find1 = contains1.find();
        boolean find2 = contains2.find();

        if(find1 && find2){
            int compareContains = contains1.end() - contains2.end();
            if (compareContains == 0) {
                return o1.compareTo(o2);
            } else {
                return compareContains;
            }
        }else if(find1){
            return -1;
        }else if(find2){
            return 1;
        }else{
            return o1.compareTo(o2);
        } 
    } 
};
Input ArrayList (search term: dog):
     

“yxcv”,   “dogb”   “多加”   “A B C D”,   “一只狗”

Output(sorted) ArrayList:
     

“多加”,   “dogb”   “一只狗”,   “A B C D”,   “yxcv”