2017-02-23 38 views
4

我有N個從存儲庫返回數據的列表。我想返回這三個列表中的第一個非空(每個執行一個不同的SQL來獲取數據)。在Java 8中懶惰地返回第一個非空列表

問題是我想懶惰地這樣做,這樣如果我已經找到可接受的結果,就不需要在數據庫上執行SQL。我的代碼是(修改)

@Override 
public List<Something> dataService(Data data) { 

return firstNonEmptyList(repository.getDataWayOne(data.getParameter()), 
         repository.getDataWayTwo(data.getParameter()), 
         repository.getDataWayThree(data.getParameter().getAcessoryParameter()) 
         Collections.singletonList(repository.getDefaultData(data.getParameter())); 

} 

@SafeVarargs 
private final List<Something> firstNonEmptyList(List<Something>... lists) { 
for (List<Something> list : lists) { 
    if (!list.isEmpty()) { 
    return list; 
    } 
} 

return null; 

} 

這是有效的,但它不是懶惰。有任何想法嗎?

+0

糾正我,如果我錯了,但我認爲這將是懶惰的,如果你包裝列表調用lambdas裏面。在'firstNonEmptyList'中,您將評估每個lambda,直到找到您想要的(即返回非空列表的那個)。非調用lambdas永遠不會執行查詢。無論如何,shmosel提供的解決方案可以完成我所描述的任務,並且更加優雅。 – Gabriel

回答

9

可以使供應商流,並評價它們在遭遇訂單,直到你找到一個結果:

return Stream.<Supplier<List<Something>>>of(
      () -> repository.getDataWayOne(data.getParameter()), 
      () -> repository.getDataWayTwo(data.getParameter()), 
      () -> repository.getDataWayThree(data.getParameter().getAcessoryParameter()), 
      () -> Collections.singletonList(repository.getDefaultData(data.getParameter())) 
     ) 
     .map(Supplier::get) 
     .filter(l -> !l.isEmpty()) 
     .findFirst() 
     .orElse(null); 

每個供應商定義瞭如何訪問一個結果集,而無需實際嘗試,直到map()執行。由於filter()map()stateless操作,因此每個供應商都將被調用,並在嘗試下一個供應商之前驗證其結果。如果找到非空結果,則該流將立即終止,因爲findFirst()short-circuiting

+1

這是一些讓人們頭暈目眩的Java 8答案之一。如果你只能一步一步解釋它,爲什麼它是懶加載? –

+0

這是一個很好的答案,最Java8-ic的方式,請原諒我的法語! – khachik

+0

@GrzegorzGórkiewicz我試過了。希望現在更清楚一點。 – shmosel

3

如果流不是你的一杯茶,你仍然可以使用lambdas達到你想要的只需對原始代碼進行一些微小的修改。

public List<Something> dataService(Data data) { 
    return firstNonEmptyList(
      () -> repository.getDataWayOne(data.getParameter()), 
      () -> repository.getDataWayTwo(data.getParameter()), 
      () -> repository.getDataWayThree(data.getParameter().getAcessoryParameter()), 
      () -> Collections.singletonList(repository.getDefaultData(data.getParameter())) 
     ); 
} 

private final List<Something> firstNonEmptyList(Supplier<List<Something>>... listSuppliers) { 
    for (Supplier<List<Something>> supplier : listSuppliers) { 
     List<Something> list = supplier.get(); 
     if (!list.isEmpty()) { 
      return list; 
     } 
    } 
    return null; 
} 
+0

巧合,我最後的評論正是你在這裏所做的。它工作(我猜),但問題是它隱藏了懶惰的意圖。在這種情況下,歡迎使用更實用的風格。 – Gabriel

+0

實際上我認爲情況正好相反,'firstNonEmptyList'的短路行爲很明顯。對於流,只有在你非常熟悉Stream API時纔是顯而易見的,而不是每個人都是。加上Javadocs的方法可以用來幫助它更加明顯。 –

+0

這也工作並通過了我所有的測試,包括懶加載。我很感激。我會用shmosel的,因爲它消除了輔助方法。 –