2016-10-15 32 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

我認爲在調用.parallel()後缺少.map(String :: toLowerCase) –

0

你也可以使用一個樹的存儲:它會非常有效的這類搜索。如果你被列入清單,以前的回答是一種方法。

0

如果您有更多或更少的靜態列表並經常執行搜索操作,則可以對列表進行排序或使用TreeMap。

此外,您不需要創建新列表並獲取其大小。你可以簡單地創建一個計數器變量並增加它。

0

刪除所有不啓動,帶「T」像這樣的項目:

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

你可以複製你的清單,並刪除不帶「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"); 

讓客戶數,帶 「T」

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); 

在Eclipse中集合的串行API急於按默認,這就是爲什麼我叫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); 

在Eclipse館藏asParallel() API是懶惰的默認。 API迫使您傳遞一個ExecutorService和一個int batchSize。這使您可以完全控制並行性。

您還可以在Eclipse集合中使用Stream API和所有MutableCollections,因爲它們擴展爲java.util.Collection

注意:我是Eclipse集合的提交者。

相關問題