2016-03-19 111 views
2

我需要找出屬於另一個元素的最大元素。給你一個例子是最清楚的。基於另一個列表中元素的最大值

我有一個包含以下數據List<String> lines

1, 1, A, Aaa ... 
1, 2, A, Aaa ... 
1, 4, A, Aaa ... 
2, 5, B, Bbb ... 
2, 3, B, Bbb ... 
3, 6, C, Ccc ... 
4, 7, D, Ddd ... 
5, 8, E, Eee ... 
1, 9, A, Aaa ... 
4, 10, D, Ddd ... 

需要明確的是,兩對夫婦的數字永遠不會相同,所以你永遠不會得到:

1, 9, A, Aaa ... 
1, 9, B, Bbb ... 

我的目標是提取線最大的第二行屬於第一行。確切的說這些行:

1, 9, A, Aaa ... 
2, 5, B, Bbb ... 
3, 6, C, Ccc ... 
4, 10, D, Ddd ... 
5, 8, E, Eee ... 

爲了證明你這不是一門功課,我已經使用多個for-loop找到最大和存儲變量的解決方案。不過,我不知道它是否有效,因爲測試了大量數據(200 000+)。

// List "lines" is declared above 

List<List<String>> data = new ArrayList<>(); 
List<List<String>> maxValues = new ArrayList<>(); 

// clear and separate to clear comparable parts 
for (String s: lines) { 
    String parts[] = s.trim().replace("\"", "").split(";"); 
    List newList = Arrays.asList(parts); 
    data.add(newList); 
} 

// naïve algorithm to find the maximum dependent to the another one 
// not sure if working 
for (List l: data) { 
    int id = Integer.parseInt(l.get(0).toString()); 
    int max = 0;  
    List<String> tempMaxValues = new ArrayList<>(); 
    for (int i=0; i<data.size(); i++) { 
     if (Integer.parseInt(l.get(0).toString()) == id) { 
      int temp = Integer.parseInt(l.get(1).toString()); 
      if (temp > max) { 
       max = temp; 
       tempMaxValues = l; 
      } 
     } 
    } 
    maxValues.add(tempMaxValues); 
} 

此外,我需要做更多的計算結果。只有用Stream或更簡單的方法纔有可能達到我想要的結果?即使在我的代碼中,我也迷迷糊糊。

+0

有沒有不好的要求幫助做功課,不好的是沒有努力來制定自己的解決方案:) –

+0

恐怕我不明白「屬於第一行的第二行的最大值」的含義。你可以擴展這個嗎? –

+0

@Sasha Salauyou:這確實不是一項家庭作業。如果是這樣,我可以自由承認這一點。 :)我這樣做是我在以前的工作中遇到的挑戰,但我們在這種情況下使用了更好的SQL。 –

回答

4

在功能上,你想要的是將每個列表值按它們的第一個元素進行分組,並且只選擇關於第二個元素的最大值。使用Stream API,您可以:

  1. 使用groupingBy(classifier, downstream)收集器按列表的第一個元素進行分組。
  2. 將下游收集器應用於歸類到同一個關鍵字的所有值是maxBy(comparator),它只選擇收集值的最大值。在這種情況下,比較器將每個列表的第二個值作爲int與內置的comparingInt的幫助進行比較。
  3. 由於maxBy返回Optional在沒有采集值的情況下,我們一起collectingAndThen(finisher)呼叫在整理致電檢索Optional值(我們知道在這種情況下,至少有一個值會被歸類)把它包Optional.get()
  4. 最後,我們只保留values()Map<String, List<String>>,因爲這返回Collection<List<String>>,我們基於它創建一個ArrayList

示例代碼:

List<List<String>> maxValues = new ArrayList<>(
    data.stream() 
     .collect(Collectors.groupingBy(
      l -> l.get(0), 
      Collectors.collectingAndThen(
       Collectors.maxBy(Comparator.comparingInt(l -> Integer.parseInt(l.get(1)))), 
       Optional::get 
      ) 
     )) 
     .values() 
); 

導致

[1, 9, A, Aaa], [2, 5, B, Bbb], [3, 6, C, Ccc], [4, 10, D, Ddd], [5, 8, E, Eee]] 

您的樣本數據。

+0

謝謝你的回答,我嘗試一下。這個複雜的國王對我來說非常複雜,我需要花一些時間來理解它。除了嘗試和嘗試之外,你還會建議我學習流的最佳方式嗎? –

+1

@NikolasCharalambidis Oracle有一個很好的教程,在這裏https://docs.oracle.com/javase/tutorial/collections/streams/開始使用Stream API。 – Tunaki

3

對於我來說,「天真」是一樣的東西用Map.merge()收集通過獨特的鍵線(ID值):

static final Function<List<String>, Integer> GET_ID = l -> Integer.parseInt(l.get(0)); 
static final Function<List<String>, Integer> GET_TEMP = l -> Integer.parseInt(l.get(1)); 

Map<Integer, List<String>> max = new TreeMap<>(); 
for (List<String> l : data) 
    max.merge(GET_ID.apply(l), l, BinaryOperator.maxBy(Comparator.comparing(GET_TEMP))); 

之後,只有具有相同ID的行中最大的第二個值線,將存儲在max地圖中。

+0

謝謝你的回答,我試試:)爲什麼你使用了'TreeMap'?你能解釋我嗎? –

+0

@NikolasCharalambidis'TreeMap'返回按鍵排序的條目(在你的情況下,通過「id」),並實現'SortedMap'和'NavigableMap',允許範圍查詢,下一個/上一個鍵等。 –

+0

非常簡潔+1。我想你可以通過將'merge'和'BinaryOperator.maxBy'組合來縮短它。 –

0

如果我明白你可以使用其他方法解決你的問題; 第一:創建一個包含您的數據(對象)類

public class DataObject { 
     int n1; 
     int n2; 
     String s1; 
     String s2; 
} 

,並創建對象的列表:

List<DataObject> data = new ArrayList<DataObject>(); 
List maxVal = new ArrayList<DataObject>(); 

for (DataObject dO1 : data){ 
    for (DataObject dO2 : data){ 
     if (dO1.n1 == dO2.n2){ 
      /*test to determin the max value and 
      *store it in maxVal 
      */ 
     } 
    } 
} 

而且將獲得的數據更容易,還是我弄錯了?

+0

我一直在考慮如何將數據存儲到對象中。但是,由於我應用於輸入文件的外部因素(更改列順序),我拒絕了它。因此,將數據存儲到對象中會造成麻煩。此外,你有錯誤的比較,你有使用雙'==' –

1

另一種方法是使用toMap收集器和BinaryOperator.maxBy作爲合併函數。鑑於List<String> lines作爲輸入,你可以得到最好的字符串是這樣的:

Collection<String> maxValues = lines.stream() 
     .collect(Collectors.toMap(
       l -> l.split(",", 2)[0], 
       l -> l, 
       BinaryOperator.maxBy(Comparator.comparingInt(
         l -> Integer.parseInt(l.split(",", 3)[1].trim()))))).values(); 
System.out.println(maxValues); 

也許它看起來更好,當合並操作被提取到變量:如果你有List<List<String>>作爲輸入

BinaryOperator<String> maxBy = BinaryOperator.maxBy(Comparator.comparingInt(
      l -> Integer.parseInt(l.split(",", 3)[1].trim()))); 
Collection<String> maxValues = lines.stream() 
     .collect(Collectors.toMap(l -> l.split(",", 2)[0], l -> l, maxBy)).values(); 

(分裂和修剪已經執行),你可以通過以下方式找到Collection<List<String>>

BinaryOperator<List<String>> maxBy = BinaryOperator.maxBy(Comparator 
     .comparingInt(l -> Integer.parseInt(l.get(1)))); 
Collection<List<String>> maxValues = lines.stream() 
     .collect(Collectors.toMap(l -> l.get(0), l -> l, maxBy)).values(); 
+0

謝謝你的答案。你能否介紹一下'BinaryOperator'?任何頁面在哪裏解釋得很好? –

+0

@NikolasCharalambidis,官方[javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/function/BinaryOperator.html)涵蓋了它非常好。 –

相關問題