2016-05-24 11 views
1

私は、ワイルドカード文字(?で表される)を含む8文字の文字列のリストを持っています。ワイルドカード文字もあります。前記文字列は4つのアルファベット文字(A〜Z)と4つの数字(0〜9)で構成されている。私はA?CD12?4は、入力として、比較は、すべての比較でtrueに解決する必要があります与える場合Javaの両側にあるワイルドカード文字との双方向文字列一致

ABCD1234 
A??D123? 
A??????? 
?BC1234? 

:それは簡単に理解できるようにするため、ここでは文字列のセットの例です。

現在の実装では、文字列をキーとして使用し、同じ文字列を正規表現としてマッピングされたオブジェクトとして解析して、Hashmapを使用しています。例えばA?CD12?4についてA([A-Z]|\\?)CD12([0-9]|\\?)4となり、その後、互換性のある文字列のセットを取得するには、次のコードを使用して:

Map<String, String> map = new HashMap<String, String>(); 

map.put("A???????", "A([A-Z]|\\?)([A-Z]|\\?)([A-Z]|\\?)([0-9]|\\?)([0-9]|\\?)([0-9]|\\?)([0-9]|\\?)"); 
map.put("ABCD1234", "ABCD1234"); 
map.put("A??D123?", "A([A-Z]|\\?)([A-Z]|\\?)D123([0-9]|\\?)"); 
map.put("?BCD123?", "([A-Z]|\\?)BC123([0-9]|\\?)"); 


String str = "A?CD12?4"; 
String strReg = "A([A-Z]|\\?)CD12([0-9]|\\?)4"; 

Set<Object> set = map.keySet() 
       .stream() 
       .filter(s -> str.matches(map.get(s)) || s.matches(strReg)) 
       .collect(Collectors.toSet()); 

しかし、これはまだインスタンスの入力ワイルドカード疑問符strではなく、マップの文字列上を(逃しました入力A?CD1234は、?BCD1234の場合は真となりません。逆の場合も同様です。

私は、これは文字列を反復処理によって修正するのは簡単だろう知っていますが、そう、私の解決策はを超える文字列と比較するための入力を必要とし、私の周り30 /秒のレートで入力を読みますパフォーマンスが重要です。

この処理はスレッド内で行われ、外部のやり取りによって入力がチェックする文字列のリストが変更されます(追加または削除のみ)。

+1

ない答えが、あなたのコードのパフォーマンスを改善したい場合は、パターンのに正規表現をコンパイルし、文字列ではなく、マップでそれらを保存する必要があります。ここではパラレル・ストリームを使用して高速化ソリューションです一致するようにフィルタを調整します。 – haggisandchips

+0

実行中に文字列のリストが変更される可能性があります。この制約を元の質問に追加します。 – fcm

+0

もしそれが**変化することができれば**私のポイントはまだ立つ。同じ正規表現が2回以上マッチしている場合、それはそれに値する単純な最適化です。 – haggisandchips

答えて

2

一般的に、?ワイルドカードは、文字列を比較するときに無視できます。これはすべてのパターンに使用できるので、正規表現の代替をマップに格納する必要はなく、文字をスキップできると反復するときに推測できます。その後、同じstrRegのためにと -

Set<String> patterns = new HashSet<>(); 

patterns.add("A???????"); 
patterns.add("ABCD1234"); 
patterns.add("A??D123?"); 
patterns.add("?BCD123?"); 

String s = "A?CD12?4"; 

Set<String> matches = patterns.parallelStream() // the main benefit of this 
           .filter(p -> { 
            for (int i = 0; i < s.length(); i++) { 
             char a = s.charAt(i), 
              b = p.charAt(i); 
             if (a != '?' && b != '?' && a != b) 
              return false; 
            } 
            return true; 
           }).collect(Collectors.toSet()); 
+0

これは、ワイルドカードがリスト文字列の側にあるとみなしますが、入力時には考慮しません。入力がA?CD1234の場合、たとえば?BCD1234の入力は失敗します。 Bと一致しません。 – fcm

+0

さて、私は、一致するかどうかをテストする前に、各パターンへの入力にすべてのワイルドカードをスーパーチャージするように答えを更新しました。 – 4castle

+1

チャンスがあれば、現在のソリューションのパフォーマンスがどういうものなのかが不思議です。私は50,000の文字列を持っていないのでテストできません。私はちょうど推測することができます。 – 4castle

1

私は正規表現を使用しません。 2つの文字列の文字を直接比較するだけです:

boolean formatCorrect(String a) { 
if (a.length() != 8) return false; 
    for (int i = 0; i < 4; ++i) { 
    char ca = a.charAt(i); 
    if (ca != '?' && !Character.isLetter(ca)) { 
     return false; 
    } 
    } 
    for (int i = 4; i < 8; ++i) { 
    char ca = a.charAt(i); 
    if (ca != '?' && !Character.isDigit(ca)) { 
     return false; 
    } 
    } 
    return true; 
} 

boolean stringsMatch(String a, String b) { 
    if (!formatCorrect(a) || !formatCorrect(b)) { 
    // Handle this. Maybe an IllegalArgumentException? 
    } 
    for (int i = 0; i < 8; ++i) { 
    char ca = a.charAt(i); 
    char cb = b.charAt(i); 
    if (ca != '?' && cb != '?' && ca != cb) return false; 
    } 
    return true; 
} 

これはオブジェクトを割り当てていないため非常に高速です。

いくつかのチェックをループから移動することで最適化することができます(たとえば、abという文字列が正しい形式であることを確認するなど)。

+0

このソリューションは、実際には非常に高速であることが証明されています(10000シーケンス入力の120000文字列のリストでテスト済み)。私はこれを私たちのソリューションに実装しようと考えています。私は実際のデータで結果を報告することに戻ります。 – fcm

関連する問題