Поиск регулярного выражения в java arraylist

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?

Обновлено: возврат элементов в порядке, но мне нужно что-то с большей производительностью, чем линейный поиск

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

erickson 21.11.2008 02:35

Тогда какую структуру данных мне следует использовать? Мое регулярное выражение всегда является префиксом.

kmilo 21.11.2008 19:08

Я рекомендую некоторую структуру данных автоматов. en.wikipedia.org/wiki/Trie

András 17.01.2015 17:47

Принципиально то, что если вы не знаете что-то об упорядочивании списка, тогда вы не может быть лучше, чем линейный поиск. Это связано с тем, что, ничего не зная об упорядочивании, чтобы найти каждый соответствующий элемент вы должны проверить каждый элемент. Если вам нужен только первый совпадающий элемент, то единственная оптимизация, которую вы можете применить, - это проверить в порядке, который позволяет вам завершить работу при первом попадании ( т.е. от первого до последнего). Если вам нужна сублинейная производительность, вы должны сообщить нам, как упорядочены ваши элементы.

DJClayworth 25.01.2017 21:31
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
16
4
44 546
7

Ответы 7

Я не верю, что есть способ сделать это с помощью 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) {..}

user38051 21.11.2008 01:02

Хорошая точка зрения. Я просто собирал все это очень быстро и не обращал на это особого внимания.

Herms 21.11.2008 01:27

К вашему сведению, <int> не является допустимым параметром типа. Вам нужно будет сделать его List <Integer>. Кроме того, когда вы используете регулярное выражение в таком цикле, вы должны скомпилировать его в объект Pattern перед входом в цикл, как это сделал DJClayworth.

Alan Moore 22.11.2008 05:06

Хм, я думал, что автобокс позаботился о int-> Integer. Ну что ж. Как я уже сказал, он не был протестирован и быстро скомпонован :)

Herms 26.11.2008 00:34

autoboxing позволит вам преобразовать .add в int и преобразовать его в Integer, но примитивный тип не может использоваться в таком параметре типа.

grinch 30.06.2014 13:32

Один из вариантов - использовать метод выбора Коллекция 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
}

Я думал вернуть фактические совпадающие строки, но вопрос был задан специально для индикаторов. Однако возврат совпадающих строк, как правило, немного чище.

Herms 21.11.2008 01:29

Это один лайнер в гуаве:

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

Другие вопросы по теме