【问题标题】:Finding the median value from a List of objects using Java 8使用 Java 8 从对象列表中查找中值
【发布时间】:2017-09-25 20:25:52
【问题描述】:

我有两个类的结构如下:

public class Company {
     private List<Person> person;
     ...
     public List<Person> getPerson() {
          return person;
     }
     ...
}

public class Person {
     private Double age;
     ...
     public Double getAge() {
          return age;
     }
     ...
}

基本上Company类有一个Person对象的List,每个Person对象都可以得到一个Age值。

如果我得到了 Person 对象的 List,有没有一种好方法可以使用 Java 8 来找到所有 Person 对象中的 Age 中值(Stream 不支持中值,但还有其他方法吗)?

Double medianAge;
if(!company.getPerson().isEmpty) {
     medianAge = company.getPerson() //How to do this in Java 8?
}

【问题讨论】:

  • 最简单的方法,不是最高效的:对列表进行排序,找到中间元素。
  • 你认为为什么需要使用流?
  • @JoeC Stream 刚刚被提到在我的构建中可以很好地使用,所以我想知道在当前技术中是否还有其他东西可以用于中值。谢谢。
  • Stream 简化了需要遍历所有元素的任务。在中位数的情况下,您只需要确保元素已排序并选择中间的一个或两个中间的。
  • 如果使用 Guava,则无需重新发明轮子:github.com/google/guava/wiki/…

标签: java list java-8 median


【解决方案1】:

您可以使用 lambda 表达式归约来获得列表的中位数:

Integer median = Person
   .stream()
   .map(Person::getAge)
   .filter(n -> n.length()/2);

【讨论】:

    【解决方案2】:

    你可以使用

    List<Person> list = company.getPerson();
    DoubleStream sortedAges = list.stream().mapToDouble(Person::getAge).sorted();
    double median = list.size()%2 == 0?
        sortedAges.skip(list.size()/2-1).limit(2).average().getAsDouble():        
        sortedAges.skip(list.size()/2).findFirst().getAsDouble();
    

    这种方法的优点是它不会修改列表,因此也不依赖于它的可变性。但是,这不一定是最简单的解决方案。

    如果你有修改列表的选项,你可以使用

    List<Person> list = company.getPerson();
    list.sort(Comparator.comparingDouble(Person::getAge));
    double median = list.get(list.size()/2).getAge();
    if(list.size()%2 == 0) median = (median + list.get(list.size()/2-1).getAge()) / 2;
    

    改为。

    【讨论】:

      【解决方案3】:

      这是@Holger 答案的简化版本,它也适用于IntStreamLongStream,并且在空流的情况下避免NoSuchElementException

      int size = someList.size();
      
      //replace 'XXX' with 'Int', 'Long', or 'Double' as desired
      return someList.stream().mapToXXX(...).sorted()
          .skip((size-1)/2).limit(2-size%2).average().orElse(Double.NaN);
      

      如果列表为空,这将返回NaN,而不是抛出NoSuchElementException。如果您更愿意使用NoSuchElementException,只需将.orElse(Double.NaN) 替换为.getAsDouble()

      【讨论】:

        【解决方案4】:

        强制番石榴方式。

        import java.util.List;
        import java.util.stream.Collectors;
        
        import com.google.common.math.Quantiles;
        
        ...
        
        List<Person> people = company.getPerson();
        List<Double> ages = people.stream().map(Person::getAge).collect(Collectors.toList());
        double median = Quantiles.median().compute(ages);
        

        尽管从 Guava 28.1 开始,Quantiles 仍被注释为 @Beta

        【讨论】:

        • 出于某种可怕的原因,@Beta 的所有内容
        【解决方案5】:

        这是在一个流中使用Collectors.collectingAndThen的另一种解决方案

        
        double median = people.stream()
                .map(Person::getAge)
                .sorted()
                .collect(Collectors.collectingAndThen(
                        Collectors.toList(),
                        ages -> {
                            int count = ages.size();
                            if (count % 2 == 0) { // even number
                                return (ages.get(count / 2 - 1) + ages.get(count / 2)) / 2;
                            } else { // odd number
                                return ages.get(count / 2);
                            }
                        }));
        

        它不会修改现有列表或要求它们进行排序。

        【讨论】:

          【解决方案6】:

          如果列表没有排序,可以使用以下代码:

          int 中间 = list.size()/2; 整数值 = list.stream().filter(i -> list.indexOf(i) == middle).collect(Collectors.toList()).get(0); System.out.println(值);

          【讨论】:

          • 这不是一个解决方案——你似乎得到了列表的中间元素,并说你自己假设列表没有排序。在这种情况下,中间元素不是中位数。这不是正确的代码——您假设列表中有一个或多个具有中间索引的元素。如果列表中的元素数量是奇数,则情况并非如此,并且 get(0) 将抛出。这效率不高——您正在遍历整个列表,然后询问每个元素它是列表中的哪个索引。也就是 O(n^2)。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-09-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多