2017-06-05 53 views
1

我有如下要求:循環在一個巨大的名單,檢查字符串等於真正

List<User> userList = listOfUsers(); // Morethan 50,000 users 

我需要找到用戶的列表中的用戶狀態。如果有任何一個用戶處於活動狀態,則打破循環。

在java中處理這個問題的有效方法是什麼?

+0

一個數據庫或通過地圖(或類似的)緩存查找 – Rogue

+0

我會建議使用一個哈希映射。也許將列表轉換爲散列表並從那裏開始。 –

+0

您需要多少次才能在同一個列表中找到活動用戶?如果只有一次,那麼最快的方法是迭代:O(n)。如果多次,那麼您可能會開始看到通過hashmaps進行O(n)初始化和O(1)查找所節省的時間 – tucuxi

回答

1

一種方式是通過在ArrayList搜索兩個方向(從上旬到中旬即從末端到中間),同時通過使用多線程,我創造了這個例子,測試它反對百萬對象/用戶,以檢查其中是否處於活動狀態(請注意,我只取得了一個用戶主動將他的中間看到最長時間搜索可能需要)。

import java.util.ArrayList; 

public class User { 
    // some fields to test 
    String name; 
    boolean active; 
    //volatile means all writes up to the volatile variable 
    //from other any thread are now visible to all other threads. 
    //so they can share working on that variable 
    static volatile boolean finishFirst = false; // to announce first thread finish 
    static volatile boolean finishSecond = false; // to announce second thread finish 
    static volatile boolean found = false; // // to announce if an active user found 

    /** 
    * Simple Constructor 
    * @param name 
    * @param active 
    */ 
    public User(String name, boolean active){ 
     this.name = name; 
     this.active = active; 
    } 


    public static void main(String[] args) { 

    // create an ArrayList of type User 
    ArrayList<User> list = new ArrayList<User>(); 

    // populate it with 1 MILLION user!! 
    int i=0; 
    for(;i<1000000; i++){ 
     // make only the one in the very middle active to prolong the search to max 
     if(i==500000){ 
      list.add(new User(String.valueOf(i),true)); 
     } 
     else{ 
      list.add(new User(String.valueOf(i),false)); 
     } 

    } 
    System.out.println("End of Adding " + i + " User"); 

    // to measure how long it will take 
    long startTime, endTime; 
    startTime = System.currentTimeMillis(); 

    System.out.println("Found Any Active: "+ isAnyActive(list)); // invoke the method 

    endTime = System.currentTimeMillis(); 
    System.out.println(endTime-startTime + " MilliScond"); 




    } 

    public static boolean isAnyActive(ArrayList<User> list){ 
     found = false; 

     // create two threads, each search the half of the array 
     // so that shall save time to half 
     Thread t1 = new Thread(new Runnable(){ 
     @Override 
     public void run() { 
      // read one more index in case the size is not an even number 
      // so it will exceed the middle in one -> no problem at all 
      for(int i=0; i<=(list.size()/2)+1; i++){ 
        if(list.get(i).active) { 
         found = true; 
         finishFirst = true; 
         break; 
        } 
      } 
      finishFirst = true; // in case did not find any 
     } 
     }); 

     // second thread the same, but read from the end to the middle 
     Thread t2 = new Thread(new Runnable(){ 
      public void run() { 
       for(int i=list.size()-1; i>=list.size()/2; i--){ 
         if(list.get(i).active) { 
          found = true; 
          finishSecond = true; 
          break; 
        } 
       } 
       finishSecond = true; 
      }  
     }); 

     // start both thread 
     t2.start(); 
     t1.start(); 

     // while one of them has not finished yet 
     while(!finishFirst || !finishSecond){ 
     // but in case not finished looping but found an active user 
      // break the loop 
       if(found){break;} 
     } 

     return found; // return the result 
    } 
} 

測試

End of Adding 1000000 User 
Found Any Active: true 
31 MilliScond 
1

有效的方法是使用SQL進行過濾,如果您使用的是。只選擇活動用戶....

當你有所有的列表與Java一起工作時,它會變慢,因爲這裏沒有魔法,你需要迭代。

public User getActiveUserFromList(userList) { 
    for (User user : userList) { 
    if (user.isActive()) { 
     return user; 
    } 
    return null; 
    } 
} 

如果你有一個清單反正命令你可以嘗試破解它,讓我們假設它是由活躍狀態

public Boolean isAnyActive(userList) { 
    if (userList.first().isActive()) { // try first 
    return true; 
    } 
    if (userList.last().isActive()) { // if its ordered and there is an active user, the last surely will be active, since first wasn't 
    return true; 
    } 
    return false; 
} 
+0

謝謝。是的,我只用這種方式,但它需要時間迭代。有沒有其他方法? – WhoAmI

+0

有一種方法就像我的回答版本lol –

+0

Java 8 Lambda有stream()。findFirst()它將返回第一個活動用戶。 – chocksaway

2

的Java 8解決方案與方法參考下令:

userList.stream().filter(User::isActive).findFirst()

它會返回Optional,所以你可以映射它。

+0

謝謝。我會考慮這一點,如果我使用8,但我使用java 7 – WhoAmI

+0

@WhoAmI其他選項將是拉蒙建議的答案。但是,我建議找到一種不同的方法,然後將> 5萬個用戶加載到應用內存中。 –

0

我一定會考慮使用Java 8 Lambda。我寫了一個例子類:

package com.chocksaway; 

import java.util.ArrayList; 
import java.util.List; 

/** 
* Author milesd on 05/06/2017. 
*/ 

class Name { 
    private String name; 
    private Boolean status; 

    public Name(String name, Boolean status) { 
     this.name = name; 
     this.status = status; 
    } 


    public String getName() { 
     return name; 
    } 

    public Boolean getStatus() { 
     return status; 
    } 
} 

public class FindFirstInStream { 
    public static void main(String[] args) { 
     List<Name> userList = new ArrayList<>(); 

     userList.add(new Name("James", false)); 
     userList.add(new Name("Eric", true)); 
     userList.add(new Name("David", false)); 

     Name firstActiveName = userList.stream() 
      .filter(e -> e.getStatus().equals(true)) 
      .findFirst() 
      .get(); 

     System.out.println(firstActiveName.getName()); 
    } 
} 

我已經創建了一個名稱類,與名稱和狀態。

我用James,Eric和David填充userList。

我使用Java 8流來過濾,並返回第一個「」活動「名稱(埃裏克)。

這存儲在「firstActiveName」中。加快搜索(沒有使用Java 8

0

您可以使用收藏ArrayDeque。ArrayDeques將使用一半的迭代來查找活動用戶。在你的情況下

ArrayDeque sample = new ArrayDeque(userList); 

    for(int i=0;i<sample.size();i++){ 
       if(sample.pollFirst().status.equalsIgnoreCase("A")) { 
        break; 
       } 

       if(sample.pollLast().status.equalsIgnoreCase("A")) { 
        break; 
       } 
       if(sample.size()==0) break; 

      } 
+0

謝謝讓我試試 – WhoAmI

+0

有趣的建議,但你需要小心,因爲它一次檢查一個項目(不是並行搜索),但確實從頭到尾都進行檢查。但是一次一個。除此之外,它們不是線程安全的,並且在數組deques中禁止使用Null元素。此外,它們不支持多線程的併發訪問。 – Yahya

0

因爲我看到許多不使用並行流的Java 8流解決方案,我添加了這個答案。你必須對你做匹配的大集合,所以你可以使用parallelStreams的力量,當你會選擇使用Java 8

Optional<User> result = userList.parallelStream().filter(User::isActive).findAny(); 

使用parallelStream將分裂流分成多個子流,這是更大的集合性能。它在內部使用ForkJoinPool來處理這些子流。唯一的區別是我在此解決方案中使用findAny()而不是findFirst()

這是Javadoc中不得不說的findAny()

此操作的行爲是明確不確定性;它是 自由選擇流中的任何元素。這是爲了允許最大 表現在並行操作;成本是多個 調用在同一來源可能不會返回相同的結果。 (如果 穩定的結果是所需的,使用的FindFirst()代替。)

這裏是從Oracle一個很好tutorial on Parallelism

相關問題