2016-03-19 82 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)涵盖了它非常好。 –

相关问题