2016-10-15 4 views
5

У меня есть список массивов содержит тысячи данных.Список строк получить элемент, начинающийся без цикла

Для примера:

List<String> custNames = new ArrayList<String>(); 
custNames.add("John"); 
custNames.add("Tom"); 
custNames.add("Bart"); 
custNames.add("Tim"); 
custNames.add("Broad"); 

Теперь я хочу, чтобы получить количество имен только начиная с 'T'. Я использовал механизм петли для моего решения.

List<String> filterNames = new ArrayList<String>(); 
String nameStarts="T"; 
for(int i=0;i<custNames.size();i++) 
{ 
    if(custNames.get(i).toLowerCase().startsWith(nameStarts.toLowerCase())) 
    { 
     filterNames.add(custNames.get(i)); 
    } 
} 
System.out.println(filterNames.size()); 

Но у меня очень большой набор данных в этом списке custNames. Есть ли другое решение без использования цикла?

Спасибо.

ответ

5

Существует очень хорошее решение для Java 8 для вашей проблемы.

Попробуйте это,

long filterNameCount = custNames 
     .stream() 
     .parallel() 
     .filter((s) -> s.startsWith(nameStarts.toLowerCase())) 
     .count(); 

System.out.println(filterNameCount); 
+0

используя .stream(). Parallel() получите значительное улучшение производительности. – Kushan

+0

Будьте очень осторожны. Если ваш вход не очень большой, использование функции parallel() будет активно ухудшать производительность и замедлять работу кода. –

+1

Я думаю, что вам не хватает .map (String :: toLowerCase) после вызова .parallel() –

0

Вы также можете использовать для хранения дерева: было бы очень эффективным для этого вида поиска. Если вы застряли в списке, то предыдущий ответ - это способ сделать.

0

Если у вас более или менее статический список и вы выполняете операцию поиска, вы можете сортировать свой список или использовать TreeMap.

Также вам не нужно создавать новый список и затем получать его размер. Вы можете просто создать переменную счетчика и увеличить ее.

0

удалить все элементы, которые не начинает с «Т», как это:

custNames.removeIf(p->!p.startsWith("T")); 

вы можете сделать копию из списка и удалить предметы, не начинающиеся с «Т».

+0

Что заставило вас думать, что это дает улучшение производительности? – talex

0

Во-первых, вы можете сократить свою инициализацию с помощью Arrays.asList(T); Во-вторых, я бы использовал простой цикл для построения таблицы подсчетов после, а затем использовал это для определения последующих запросов. Нечто подобное,

List<String> custNames = new ArrayList<String>(Arrays.asList("John", "Tom", 
     "Bart", "Tim", "Broad")); 
int[] counts = new int[26]; 
for (String name : custNames) { 
    char ch = Character.toLowerCase(name.charAt(0)); 
    counts[ch - 'a']++; 
} 
for (int i = 0; i < counts.length; i++) { 
    if (counts[i] > 0) { 
     System.out.printf("There are %d words that start with %c%n", 
       counts[i], (char) ('a' + i)); 
    } 
} 

Какие выходы

There are 2 words that start with b 
There are 1 words that start with j 
There are 2 words that start with t 

Или, в данном конкретном случае - counts['t' - 'a'] является счетчиком слов, начинающихся с t.

0

Если заказ, в котором хранятся элементы, не имеет значения, вы можете сохранить имена в HashMap, где первым символом каждого имени является ключ, а ArrayList имен с этим первым символом - это значения. И тогда все, что вам нужно сделать, если HashMap называется customerList, является customerList.get («T»). Size().

Инициализация HashList и добавление клиентов

HashMap<Character, ArrayList<String>> customerList = new HashMap<Character, ArrayList<String>>(); 
int NUM_ALPHABETS = 26; 
int ascii_char = 97; 
for(int i = 0; i < NUM_ALPHABETS; i++){ 
    char c = (char) ascii_char; 
    customerList.add(c, new ArrayList<String>()); 
    ascii_char++; 
} 

customerList.get("t").add("Tony"); 
customerList.get("a").add("Alice"); 
customerList.get("b").add("Ben"); 

Getting Количество клиентов Начиная с "т"

int num_t = customerList.get("t").size(); 
0

Вы можете создать свою собственную сортировку и найти реализацию.

Рассмотрим следующий пример:

public class ContainingArrayList<E> extends ArrayList<E> { 
    private Comparator<E> comparator; 

    public ContainingArrayList(Comparator<E> comparator) { 
     this.setComparator(comparator); 
    } 

    @Override 
    public boolean add(E e) { 
     // If the collection is empty or the new element is bigger than the last one, append it to the end of the collection 
     if(size() == 0 || comparator.compare(e, get(size()-1)) >= 0) 
      return super.add(e); 
     else { 
      for (int i = 0; i < size(); i++) { 
       int result = comparator.compare(e, get(i)); 
       // If the new element is bigger than the current element, continue with the next element 
       if (result > 0) continue; 
       // If the new element is equal to the current element, no need to insert (you might insert of course) 
       if (result == 0) return false; 
       // Otherwise the new element is smaller than the current element, so insert it between the previous and the current element 
       super.add(i, e); 
       return true; 
      } 
      return super.add(e); 
     } 
    } 

    public E get(E containingElement) { 
     int start = 0; 
     int end = size()-1; 
     // If the element is the first one, return the first element 
     if(comparator.compare(containingElement, super.get(start)) == 0) 
      return super.get(start); 
     // If the element is the last one, return the last element 
     if(comparator.compare(containingElement, super.get(end)) == 0) 
      return super.get(end); 

     // Otherwise do a binary search 
     while(start != end) { 
      // Get the element between start and end positions 
      E mid = super.get(start + (end/2)); 
      // Compare the two elements 
      int result = comparator.compare(containingElement, mid); 
      // If the middle element compared to the containing element is equal, return the middle element 
      if(result == 0) { 
       return mid; 
      } 
      // If the containing element is smaller than the middle, halve the end position 
      else if(result < 0) { 
       end = start + (end/2); 
      } 
      // If the containing element is bigger than the middle, set the start position to the middle position 
      else if(result > 0) { 
       start = start + (end/2); 
      } 
     } 
     return null; 
    } 


    public Comparator<E> getComparator() { 
     return comparator; 
    } 

    public void setComparator(Comparator<E> comparator) { 
     this.comparator = comparator; 
    } 
} 

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

Тест:

public class SortFindTest { 

    public SortFindTest() { 
     ContainingArrayList<String> t = new ContainingArrayList<String>(new MyComparator()); 
     t.add("John"); 
     t.add("Tom"); 
     t.add("Bart"); 
     t.add("Tim"); 
     t.add("Broad"); 

     System.out.println(t.get("T")); 
    } 

    class MyComparator implements Comparator<String> { 
     @Override 
     public int compare(String o1, String o2) { 
      int o1c = o1.charAt(0); 
      int o2c = o2.charAt(0); 
      if(o1c == o2c) 
       return 0; 
      if(o1c > o2c) 
       return 1; 
      return -1; 
     } 

    } 

    public static void main(String[] args) { 
     new SortFindTest(); 
    } 
} 

Я не уверен, если это будет быстрее, чем Java 8 Стрит API, но это стоит попробовать.

3

Если вы открыты для использования сторонней библиотеки, есть несколько интересных вариантов, которые вы можете использовать с Eclipse Collections.

Если вы используете ArrayList, как вы его выше, вы можете использовать утилиту LazyIterate следующим образом:

int count = LazyIterate.collect(custNames, String::toLowerCase) 
     .countWith(String::startsWith, nameStarts.toLowerCase()); 
Assert.assertEquals(2, count); 

Если вы используете замены Eclipse, Коллекции для ArrayList, вы можете использовать богатые функциональные протоколы, доступные непосредственно на MutableList:

MutableList<String> custNames = 
     Lists.mutable.with("John", "Tom", "Bart", "Tim", "Broad"); 
String nameStarts= "T"; 
int count = custNames.asLazy() 
     .collect(String::toLowerCase) 
     .countWith(String::startsWith, nameStarts.toLowerCase()); 
System.out.println(count); 
Assert.assertEquals(2, count); 

серийный API в Eclipse, Collections стремится, по-умолчанию, поэтому я назвал asLazy() первым. Метод сбора в противном случае создал бы еще MutableList.

Если тест кода с вашим полным набором данных, следующая параллельная версия кода может быть более производительным:

MutableList<String> custNames = 
     Lists.mutable.with("John", "Tom", "Bart", "Tim", "Broad"); 
String nameStarts= "T"; 
int processors = Runtime.getRuntime().availableProcessors(); 
int batchSize = Math.max(1, custNames.size()/processors); 
ExecutorService executor = Executors.newFixedThreadPool(processors); 
int count = custNames.asParallel(executor, batchSize) 
     .collect(String::toLowerCase) 
     .countWith(String::startsWith, nameStarts.toLowerCase()); 
executor.shutdown(); 
Assert.assertEquals(2, count); 

asParallel() API в Eclipse, Коллекции ленив-по-умолчанию. API заставляет вас пройти в ExecutorService и int партиях. Это дает вам полный контроль над параллелизмом.

Вы также можете использовать Stream API со всеми MutableCollections в коллекциях Eclipse, поскольку они расширяют java.util.Collection.

Примечание: Я являюсь коммиттером для коллекций Eclipse.

Смежные вопросы