Finding median value from a list of objects using Java 8

I have two classes that are structured like this:

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

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

      

Basically, the Company class has List of Person objects, and each Person object can get an Age value.

If I get List of Person objects, is there a good way to use Java 8 to find the median Age among all Person objects (Stream does not support median, but is there anything else)?

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

      

+6


source to share


4 answers


you can use

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();

      

The advantage of this approach is that it does not modify the list, and therefore does not rely on its mutability either. However, this is not necessarily the simplest solution.



If you have the option to change the list you can use

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;

      

instead.

+9


source


Mandatory guava path.

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);

      



Although as of Guava 28.1 Quintiles is still celebrated as @Beta .

+3


source


Here's a simplified version of @Holger's answer that also works for IntStream

and LongStream

, and avoids NoSuchElementException

in the case of an empty stream:

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);

      

This will return NaN

if the list is empty rather than throwing NoSuchElementException

. If you would rather NoSuchElementException

throw it away instead , just replace .orElse(Double.NaN)

with .getAsDouble()

.

+2


source


You can use shorthand lambda expression to get the median of the list. Something like that

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

;

-five


source







All Articles