#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 не имело большого значения в контексте этого ответа, но я все равно добавил их.