Как сопоставить 3 параметра, включая совпадения с подстановочными знаками?

#java

#java

Вопрос:

Я хочу определить фильтр черного списка, который состоит из значений. Пусть они будут a, b, c в таком порядке.

Для быстрого поиска я подумал о том, чтобы объединить их в Set<String> , чтобы я мог быстро искать blacklist.contains(a b c) . Который в целом работает нормально.

Проблема: любой из 3 параметров, кроме одного, в определении черного списка, может быть подстановочным * . Таким образом, черный список может, например, содержать:

 OneTwoThree
*bc
  

В результате выполняется вызов метода isBlacklisted("One", "b", "c") = true .
Примечание: входной параметр метода, конечно, может содержать не подстановочные знаки, а только простые строковые слова.

Вопрос в том, как я мог бы лучше написать поиск следующим образом?

 private Set<String> blacklist; //concatenation of 3 fields 'a b c'

private boolean isBlacklisted(String a, String b, String c) {
    return blacklist.contains(a b c)
        || blacklist.contains("*" b c)
        || blacklist.contains(a "*" c)
        || blacklist.contains(a b "*")
        || blacklist.contains("**" c)
        || blacklist.contains(a "**")
        || blacklist.contains("*" b "*");
}
  

Как я могу создать быстрый фильтр, поддерживающий подстановочные знаки, без необходимости проверять все возможные объединенные подстановочные знаки, как я сделал?

Комментарии:

1. Я не понимаю. Если a это подстановочный знак, в чем будет разница между a b c и "*" b c ?

2. Позвольте мне перефразировать: черный список может содержать параметр, определенный как подстановочные знаки. Входные параметры всегда являются простыми строками и никогда не являются подстановочными знаками.

3. Если один элемент вашего черного списка является шаблоном, почему бы просто не сравнить с двумя другими элементами?

4. Следует isBlackListed возвращать true для аргументов "OneTw", "oTh", "ree" , если они blackList содержатся "OneTwoThree" (в настоящее время это так)

5. Запрос на примере (метод a) использует ту же логику, но вместо подстановочного знака a null . Он используется, например, с java JDBC, DatabaseMetaData для запроса структуры базы данных.

Ответ №1:

Когда вы пишете *, вы имеете в виду «любую строку»?
Если да — просто используйте регулярное выражение:

 for (String blacklist : blackRegex)
{
    Pattern p = Pattern.compile(blackRegex);
    Matcher matcher = p.matcher(string);
    if (matcher.find()
    {
           return true;
    }
}

return false;
  

примером blackRegex является (someA)(someB).*

Используйте .* а не * для любой строки, вы можете следовать руководству по регулярным выражениям Java.

Комментарии:

1. Еще лучше было бы предварительно скомпилировать регулярные выражения и сохранить их в Set<Pattern>

2. Безусловно, сначала хотел дать решение

3. @membersound — я ответил на твой вопрос ? 🙂

Ответ №2:

Благодаря всем вашим предложениям, я закончил следующим образом (предполагая, что определение черного списка исходит из базы данных): для каждого кортежа черного списка «abc» я создаю шаблон регулярных выражений. Затем эти шаблоны объединяются с OR | .

 private void init(SqlRowSet set) {
        Set<String> patterns = new HashSet<>();
        while (set.next()) {
            String a = set.getString(1);
            String b = set.getString(2);
            String c = set.getString(3);

            patterns.add(a   "/"   b   "/"   c);
        }

        regex = Pattern.compile(String.join("|", patterns));
}

private boolean isBlacklisted(String a, String b, String c) {
    return regex.matcher(a "/" b "/" c).matches();
}
  

Теперь я могу создать свой черный список с помощью a, b, .* и сопоставить это с isBlacklisted("a", "b", "anything") = true , например.

Ответ №3:

Определите класс BlackListRule :

 class BlackListRule {
    private String a;
    private String b;
    private String c;

    BlackListRule(String a, String b, String c) {
        this.a = a; this.b = b; this.c = c;
    }

    public boolean matches(String a, String b, String c) {
        return ("*".equals(this.a) || this.a.equals(a))
             amp;amp;("*".equals(this.b) || this.b.equals(b))
             amp;amp;("*".equals(this.c) || this.c.equals(c));
    }

    public int hashCode() {
        return Arrays.deepHashCode(new char[][]{a.toCharArray(), b.toCharArray(), c.toCharArray()});
    }

    public boolean equals(Object o) {
        return o instanceof BlackListRule amp;amp; ((BlackListRule)o).hashCode() == hashCode();
    }
}
  
 private Set<BlackListRule> blacklist;
private boolean isBlacklisted(String a, String b, String c) {
    return blacklist.stream().anyMatch(rule -> rule.matches(a,b,c));
}
  

Это минимальный рабочий пример, и вам определенно следует его улучшить, но вы поняли идею.

Вы можете попробовать это здесь.

Комментарии:

1. @Lino не имело большого значения в контексте этого ответа, но я все равно добавил их.