我有如下要求:循環在一個巨大的名單,檢查字符串等於真正
List<User> userList = listOfUsers(); // Morethan 50,000 users
我需要找到用戶的列表中的用戶狀態。如果有任何一個用戶處於活動狀態,則打破循環。
在java中處理這個問題的有效方法是什麼?
我有如下要求:循環在一個巨大的名單,檢查字符串等於真正
List<User> userList = listOfUsers(); // Morethan 50,000 users
我需要找到用戶的列表中的用戶狀態。如果有任何一個用戶處於活動狀態,則打破循環。
在java中處理這個問題的有效方法是什麼?
一種方式是通過在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
有效的方法是使用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;
}
謝謝。是的,我只用這種方式,但它需要時間迭代。有沒有其他方法? – WhoAmI
有一種方法就像我的回答版本lol –
Java 8 Lambda有stream()。findFirst()它將返回第一個活動用戶。 – chocksaway
的Java 8解決方案與方法參考下令:
userList.stream().filter(User::isActive).findFirst()
它會返回Optional
,所以你可以映射它。
謝謝。我會考慮這一點,如果我使用8,但我使用java 7 – WhoAmI
@WhoAmI其他選項將是拉蒙建議的答案。但是,我建議找到一種不同的方法,然後將> 5萬個用戶加載到應用內存中。 –
我一定會考慮使用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)
您可以使用收藏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;
}
因爲我看到許多不使用並行流的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。
一個數據庫或通過地圖(或類似的)緩存查找 – Rogue
我會建議使用一個哈希映射。也許將列表轉換爲散列表並從那裏開始。 –
您需要多少次才能在同一個列表中找到活動用戶?如果只有一次,那麼最快的方法是迭代:O(n)。如果多次,那麼您可能會開始看到通過hashmaps進行O(n)初始化和O(1)查找所節省的時間 – tucuxi