【问题标题】:Using binarySearch with Comparator and regex将 binarySearch 与 Comparator 和 regex 一起使用
【发布时间】:2010-08-11 08:55:46
【问题描述】:

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

老办法:

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

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

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

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

【问题讨论】:

  • 如果您每次都必须对列表进行排序,那将不如“旧方法”那么有效。

标签: java search comparator


【解决方案1】:

这应该可行:

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

【讨论】:

  • 谢谢。你回答了所有问题。我会保留我的单循环
【解决方案2】:

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

【讨论】:

  • 否,如果二进制搜索首先找到“contact.345”,则该项目之前可能有“contact.1”和“contact.2”,而您首先找到的项目之后可能有“contact.4”。
【解决方案3】:

我认为从性能的角度来看,您现在执行此操作的方式实际上是最好的方式。排序本身可能比简单地遍历未排序列表更昂贵。但是为了确保您必须运行一些测试(尽管由于 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;
    }
}

【讨论】:

  • 是的,这就是标准。 startsWith 是一个底层的正则表达式。
【解决方案4】:

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

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

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


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

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

【讨论】:

    【解决方案5】:

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

    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", “狗”, “狗”, “A B C D”, “一条狗”

    Output(sorted) ArrayList:
    

    “狗”, “狗”, “一只狗”, “A B C D”, "yxcv"

    【讨论】:

      猜你喜欢
      • 2020-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-24
      • 1970-01-01
      • 2017-02-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多