2016-03-29 20 views
7

我正在尋找一種簡潔的方式來篩選列表中特定索引處的項目。我的例子輸入如下所示:特定索引的Java流篩選器項目

List<Double> originalList = Arrays.asList(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); 
List<Integer> filterIndexes = Arrays.asList(2, 4, 6, 8); 

我想篩選出在指數2468項目。我有一個for循環,跳過匹配索引的項目,但我希望有一個使用流的簡單方法。最後的結果會看起來像:

List<Double> filteredList = Arrays.asList(0.0, 1.0, 3.0, 5.0, 7.0, 9.0, 10.0); 
+7

使用'for'循環和'remove(int)'這非常簡單。不要爲所有事情使用流。 –

+0

謝謝@PaulBoddington。來自.NET的工作,我習慣使用LINQ來處理大部分數組/列表操作,並且使用LINQ很容易做到這一點,所以希望在Java 8流中有類似的東西。 – Vedanth

+6

Streams和_any_類型的索引不會相處得很好。 –

回答

14

您可以生成一個IntStream模仿原始列表的索引,然後刪除那些處於filteredIndexes列表,然後將這些指標映射到其對應的元素列表(更好的方法是爲索引指定,因爲它們根據定義是唯一的,因此contains是恆定時間操作)。

List<Double> filteredList = 
    IntStream.range(0, originalList.size()) 
      .filter(i -> !filterIndexes.contains(i)) 
      .mapToObj(originalList::get) 
      .collect(Collectors.toList()); 
4

如果您filteredIndexes列表預先排序,就可以避免這種方式檢測,每一個元素:

List<Double> filteredList = IntStream.rangeClosed(0, filterIndexes.size()) 
    .mapToObj(idxPos -> idxPos == 0 
      ? originalList.subList(0, filterIndexes.get(idxPos)) 
      : idxPos == filterIndexes.size() 
      ? originalList.subList(filterIndexes.get(idxPos-1)+1, originalList.size()) 
      : originalList.subList(filterIndexes.get(idxPos-1)+1, filterIndexes.get(idxPos))) 
    .flatMap(List::stream) 
    .collect(Collectors.toList()); 

這裏我們創建了一些子列表中包含所有的過濾指數之間的要素,然後只是將它們拼成單個最終列表。對於大量輸入(例如數百萬個數字),此解決方案的速度可能比@AlexisC提供的速度更快。

2

如果您對索引進行排序,那麼您可以使用java.util.List.remove(int)刪除項目。

List<Double> originalList = new ArrayList<>(Arrays.asList(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)); 
List<Integer> filterIndexes = Arrays.asList(2, 4, 6, 8); 

filterIndexes.stream() 

    // remove higher indixes first, because each remove affects all indexes to the right 
    .sorted(Comparator.reverseOrder()) 

    // make sure to use remove(int) not remove(Object) in java.util.List to use indexes 
    .mapToInt(Integer::intValue) 

    // remove each index 
    .forEach(originalList::remove); 

// print results 
originalList.forEach(System.out::println); 
+0

請勿使用'.mapToInt(Integer :: valueOf)'。這是有效的,但是你將'Integer'對象轉換爲'int'值以將其傳遞給'Integer valueOf(int)'以再次獲得一個Integer對象,然後將它自動拆箱爲int正在使用'mapToInt'。您確實需要'.mapToInt(i-> i)'或'.mapToInt(Integer :: intValue)',而不是執行單個拆箱。 – Holger

+0

@Holger謝謝! 'Integer :: intValue'我在找什麼。我也嘗試過'Function.identity()',但沒有奏效。 – slartidan

+0

是的,'Function.identity()'返回一個'Function'而不是一個'ToIntFunction',所以這不適用於傳統的類型兼容性規則,而不是像'x - > x直接... – Holger