簡體   English   中英

將BinarySearch與Comparator和regex結合使用

[英]Using binarySearch with Comparator and regex

我試圖編寫一個搜索List<String>的快速搜索,而不是遍歷列表並手動檢查,我想使用binarySearch來做到這一點,但是我不確定如何做到這一點。

舊方法:

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

相反,我想要這樣的東西:

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

有人可以幫我寫這個比較器嗎?
有沒有比使用binarySearch更好的方法呢?

這應該工作:

        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);

但是,排序然后進行二進制搜索比單遍迭代的效率低。

附錄:

盡管以上答案對您有所幫助,但這是另一種方式(靈感來自Google館藏的Scala):

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

只是另一個比較器(使用正則表達式):

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”

問題在於二進制搜索永遠不會回頭。 我通過使用二進制搜索找到第一個匹配元素的方法來解決此問題,然后向后循環以查找此子字符串的第一個匹配項,然后是一個收集所有匹配元素的循環。

從性能的角度來看,我認為您現在的做法實際上是最好的方法。 排序本身可能比簡單地遍歷未排序列表更昂貴。 但是要確保您必須運行一些測試(盡管由於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;
    }
}

對列表進行排序比對列表進行線性掃描要花費更多的時間。 (基於比較的排序所花費的時間與n(log n)成正比,其中n是列表的長度。)

即使大多數情況下對列表進行了完全排序 ,排序算法也必須至少迭代列表以進行檢查。

基本上,無論如何實現排序算法,該算法(即使在最佳情況下) 也必須至少查看所有元素 因此,線性搜索“ concat”可能是您的最佳選擇。


一個更詳盡的解決方案是將包含字符串的列表子類化,並維護“ concat”第一次出現的索引。

鑒於字符串是不可變的,您所要做的就是重寫add,remove等,並相應地更新索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM