使用Java 8从对象列表中查找中值

000*_*000 6 java list median java-8

我有两个结构如下的类:

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

public class Person {
     private Double age;
     ...
     public Double getAge() {
          return age;
     }
     ...
}
Run Code Online (Sandbox Code Playgroud)

基本上,Company类有一个Person对象列表,每个Person对象都可以获得Age值.

如果我得到Person对象的列表,是否有一种很好的方法可以使用Java 8在所有Person对象中找到中位数Age值(Stream不支持中位数但是还有其他内容)?

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

Hol*_*ger 9

你可以用

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();
Run Code Online (Sandbox Code Playgroud)

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

如果您可以选择修改列表,则可以使用

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;
Run Code Online (Sandbox Code Playgroud)

代替.


Vse*_*nov 7

强制性番石榴方式。

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);
Run Code Online (Sandbox Code Playgroud)

尽管截至 Guava 28.1 Quantiles仍被注释为@Beta

  • 出于某种可怕的原因,一切都是“@Beta” (2认同)

Han*_*nde 6

这是@Holger 答案的简化版本,它也适用于IntStreamand LongStream,并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);
Run Code Online (Sandbox Code Playgroud)

NaN如果列表为空而不是 throw ,这将返回NoSuchElementException。如果您更喜欢抛出 a NoSuchElementException,只需替换.orElse(Double.NaN).getAsDouble()