ArrayList <String> list = new ArrayList();
list.add("behold");
list.add("bend");
list.add("bet");
list.add("bear");
list.add("beat");
list.add("become");
list.add("begin");
Есть способ найти regexp bea. * И получить индексы, как в ArrayList.indexOf?
Обновлено: возврат элементов в порядке, но мне нужно что-то с большей производительностью, чем линейный поиск
Тогда какую структуру данных мне следует использовать? Мое регулярное выражение всегда является префиксом.
Я рекомендую некоторую структуру данных автоматов. en.wikipedia.org/wiki/Trie
Принципиально то, что если вы не знаете что-то об упорядочивании списка, тогда вы не может быть лучше, чем линейный поиск. Это связано с тем, что, ничего не зная об упорядочивании, чтобы найти каждый соответствующий элемент вы должны проверить каждый элемент. Если вам нужен только первый совпадающий элемент, то единственная оптимизация, которую вы можете применить, - это проверить в порядке, который позволяет вам завершить работу при первом попадании ( т.е. от первого до последнего). Если вам нужна сублинейная производительность, вы должны сообщить нам, как упорядочены ваши элементы.




Я не верю, что есть способ сделать это с помощью Java API, и нет способа сделать это с помощью Apache Commons. Однако скатать собственное будет несложно.
Есть встроенный метод? Не то, что я знаю о. Однако сделать это самому должно быть довольно легко. Вот некоторый полностью непроверенный код, который должен дать вам основную идею:
import java.util.regex.Pattern;
import java.util.ListIterator;
import java.util.ArrayList;
/**
* Finds the index of all entries in the list that matches the regex
* @param list The list of strings to check
* @param regex The regular expression to use
* @return list containing the indexes of all matching entries
*/
List<Integer> getMatchingIndexes(List<String> list, String regex) {
ListIterator<String> li = list.listIterator();
List<Integer> indexes = new ArrayList<Integer>();
while(li.hasNext()) {
int i = li.nextIndex();
String next = li.next();
if (Pattern.matches(regex, next)) {
indexes.add(i);
}
}
return indexes;
}
Возможно, я неправильно использовал части Pattern и ListIterator (я никогда не использовал их), но это должно дать основную идею. Вы также можете сделать простой цикл вместо цикла while над итератором.
Лично я считаю, что методы api должны принимать аргументы и возвращать значения самого абстрактного типа. Следовательно, мое педантичное исправление вашего ответа будет следующим: public List <int> getMatchingIndices (List <String> list, String regex) {..}
Хорошая точка зрения. Я просто собирал все это очень быстро и не обращал на это особого внимания.
К вашему сведению, <int> не является допустимым параметром типа. Вам нужно будет сделать его List <Integer>. Кроме того, когда вы используете регулярное выражение в таком цикле, вы должны скомпилировать его в объект Pattern перед входом в цикл, как это сделал DJClayworth.
Хм, я думал, что автобокс позаботился о int-> Integer. Ну что ж. Как я уже сказал, он не был протестирован и быстро скомпонован :)
autoboxing позволит вам преобразовать .add в int и преобразовать его в Integer, но примитивный тип не может использоваться в таком параметре типа.
Один из вариантов - использовать метод выбора Коллекция Apache Commons. Вам нужно будет создать объект Predicate (объект с единственным методом «оценки», который использует регулярное выражение для проверки совпадения и возврата true или false), а затем вы можете искать элементы в списке, которые соответствуют. Однако он не вернет индексы, он вернет коллекцию, содержащую сами элементы.
Herms правильно понял основы. Если вам нужны строки, а не индексы, вы можете улучшить его, используя цикл foreach Java 5:
import java.util.regex.Pattern;
import java.util.ListIterator;
import java.util.ArrayList;
/**
* Finds the index of all entries in the list that matches the regex
* @param list The list of strings to check
* @param regex The regular expression to use
* @return list containing the indexes of all matching entries
*/
List<String> getMatchingStrings(List<String> list, String regex) {
ArrayList<String> matches = new ArrayList<String>();
Pattern p = Pattern.compile(regex);
for (String s:list) {
if (p.matcher(s).matches()) {
matches.add(s);
}
}
return matches
}
Я думал вернуть фактические совпадающие строки, но вопрос был задан специально для индикаторов. Однако возврат совпадающих строк, как правило, немного чище.
Это один лайнер в гуаве:
final Iterable<String> matches = Iterables.filter(myStrings, Predicates.contains(Pattern.compile("myPattern")));
for (final String matched : matches) {
...
}
Это будет возрождение потока, но может быть кому-то полезно. Возможно, вам не понадобятся индексы, возможно, следующий шаг сделает что-то с элементами, которые соответствуют регулярному выражению, и поэтому вы запросили индексы. Но вы можете использовать потоки Java8 и лямбда-выражение:
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.List;
...
var pattern = Pattern.compile(define); // var is Java 10 feature
List<String> list = originalList
.stream()
.filter(e -> pattern.matcher(e).matches())
.collect(Collectors.toList());
Вы можете взять исходный список, преобразовать его в поток, запустить на нем фильтр, который запускает лямбда-выражение в соответствии с вашим шаблоном, и преобразовать его обратно в список. Но вы можете сохранить его как поток и запустить .foreach с другим лямбда-выражением.
Когда мы говорим о больших списках, имеет смысл передавать их параллельно со встроенными функциями Java8.
@Test
public void testRegexPerformance()
{
List<String> list = new ArrayList<>();
list.add("behold");
list.add("bend");
list.add("bet");
list.add("bear");
list.add("beat");
list.add("become");
list.add("begin");
for (int i = 0; i < 20; i++)
{
list.addAll(list);
}
System.out.println("Original list size: " + list.size());
Instant startTime = Instant.now();
List<String> results = testLoopApproach(list, "bea.*");
Instant current = Instant.now();
System.out.println("Found List size: " + results.size());
System.out.println("Elapsed millis: " + (current.toEpochMilli() - startTime.toEpochMilli()));
startTime = Instant.now();
results = testStreamApproach(list, "bea.*");
current = Instant.now();
System.out.println("Found List size: " + results.size());
System.out.println("Elapsed millis: " + (current.toEpochMilli() - startTime.toEpochMilli()));
}
private List<String> testStreamApproach(List<String> list, String regex)
{
Predicate<String> pred = Pattern.compile(regex).asPredicate();
return list.parallelStream().filter(pred).collect(Collectors.toList());
}
private List<String> testLoopApproach(List<String> list, String regex)
{
Pattern p = Pattern.compile(regex);
List<String> resulsts = new ArrayList<>();
for (String string : list)
{
if (p.matcher(string).find())
{
resulsts.add(string);
}
}
return resulsts;
}
and the results are:
Original list size: 7340032
Found List size: 2097152
Elapsed millis: 1785
Found List size: 2097152
Elapsed millis: 260
Вы не сможете повысить производительность, если поместите свои строки в список. Всегда ли ваше регулярное выражение является префиксом или вы хотите обрабатывать любое регулярное выражение?