4

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

老办法:

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

相反,我想要这样的东西:

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

有人可以帮我写这个比较器吗?
有没有比使用 binarySearch 更好的方法呢?

4

5 回答 5

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 缺乏对一流函数的支持。

于 2010-08-11T09:48:45.917 回答
1

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

即使列表在大多数情况下都是完全排序的,排序算法也必须至少遍历列表来检查这一点。

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


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

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

于 2010-08-11T09:48:34.123 回答
1

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

于 2010-08-11T09:00:29.393 回答
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;
    }
}
于 2010-08-11T09:34:34.810 回答
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”、“doga”、“abcd”、“一只狗”

Output(sorted) ArrayList:

“doga”、“dogb”、“一只狗”、“abcd”、“yxcv”

于 2012-08-23T22:23:31.980 回答