Статьи

Сводная статистика по классам JDK 8

В JDK 8 представлены три новых класса : DoubleSummaryStatistics , IntSummaryStatistics и LongSummaryStatistics пакета java.util . Эти классы позволяют быстро и легко вычислить общее количество элементов, минимальное значение элементов, максимальное значение элементов, среднее значение элементов и сумму элементов в коллекции значений типа double, целых или длинных. Документация Javadoc на уровне классов каждого класса начинается с одного и того же предложения, которое кратко излагает это, описывая каждое как «объект состояния для сбора статистики, такой как count, min, max, sum и medium».

Javadoc уровня класса для каждого из этих трех классов также сообщает каждому классу: «Этот класс предназначен для работы (хотя и не требует) с потоками». Наиболее очевидная причина включения этих трех типов классов SummaryStatistics заключается в использовании с потоками , которые также были представлены в JDK 8 .

Действительно, каждый из трех комментариев класса Javadoc на уровне класса также предоставляет пример использования каждого класса в сочетании с потоками соответствующего типа данных. Эти примеры демонстрируют вызов соответствующего метода Streams (Supplier, BiConsumer, BiConsumer) ( операция с изменяемым терминальным потоком ) и передача нового экземпляра класса SummaryStatistics (конструктор), принятие и объединение методов (как ссылки на метод ) в этот метод collect в качестве аргументов «поставщик», «аккумулятор» и «объединитель» соответственно.

Остальная часть этой статьи демонстрирует использование IntSummaryStatistics , LongSummaryStatistics и DoubleSummaryStatistics . Некоторые из этих примеров будут ссылаться на карту сезонов телесериала «Секретные материалы » и рейтинг Нильсена для премьеры этого сезона. Это показано в следующем листинге кода.

Объявление и инициализация xFilesSeasonPremierRatings

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/**
 * Maps the number of each X-Files season to the Nielsen rating
 * (millions of viewers) for the premiere episode of that season.
 */
private final static Map<Integer, Double> xFilesSeasonPremierRatings;
 
static
{
   final Map<Integer, Double> temporary = new HashMap<>();
   temporary.put(1, 12.0);
   temporary.put(2, 16.1);
   temporary.put(3, 19.94);
   temporary.put(4, 21.11);
   temporary.put(5, 27.34);
   temporary.put(6, 20.24);
   temporary.put(7, 17.82);
   temporary.put(8, 15.87);
   temporary.put(9, 10.6);
   xFilesSeasonPremierRatings = Collections.unmodifiableMap(temporary);
}

В следующем листинге кода используется карта, созданная в предыдущем листинге кода, демонстрируется применение DoubleSummaryStatistics к потоку части «значений» карты, и она очень похожа на примеры, представленные в Javadoc для трех классов SummaryStatistics. Класс DoubleSummaryStatistics класс IntSummaryStatistics и класс LongSummaryStatistics имеют по существу одинаковые поля, методы и API (только различия являются поддерживаемыми типами данных). Поэтому, хотя этот и многие из примеров этого поста специально используют DoubleSummaryStatistics (поскольку рейтинги Нильсена в X-Files имеют двойные значения), эти принципы применяются к двум другим интегральным типам классов SummaryStatistics.

Использование DoubleSummaryStatistics с потоком на основе коллекции

01
02
03
04
05
06
07
08
09
10
11
12
13
14
/**
 * Demonstrate use of DoubleSummaryStatistics collected from a
 * Collection Stream via use of DoubleSummaryStatistics method
 * references "new", "accept", and "combine".
 */
private static void demonstrateDoubleSummaryStatisticsOnCollectionStream()
{
   final DoubleSummaryStatistics doubleSummaryStatistics =
      xFilesSeasonPremierRatings.values().stream().collect(
         DoubleSummaryStatistics::new,
         DoubleSummaryStatistics::accept,
         DoubleSummaryStatistics::combine);
   out.println("X-Files Season Premieres: " + doubleSummaryStatistics);
}

Результат выполнения вышеприведенной демонстрации показан ниже:

1
X-Files Season Premieres: DoubleSummaryStatistics{count=9, sum=161.020000, min=10.600000, average=17.891111, max=27.340000}

В предыдущем примере класс SummaryStatistics применялся к потоку, основанному непосредственно на коллекции (часть «значения» Map ). Следующий листинг кода демонстрирует аналогичный пример, но использует IntSummaryStatistics и использует промежуточную операцию отображения потока, чтобы указать, какую функцию вызывать для объектов коллекции для заполнения объекта SummaryStatistics. В этом случае для коллекции используется объект Set<Movie> возвращаемый методом Java8StreamsMoviesDemo.getMoviesSample() и Java8StreamsMoviesDemo.getMoviesSample() в моем сообщении блога Stream-Powered Collections Functionality в JDK 8 .

Использование IntSummaryStatistics с картой потока (функция)

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * Demonstrate collecting IntSummaryStatistics via mapping of
 * certain method calls on objects within a collection and using
 * lambda expressions (method references in particular).
 */
private static void demonstrateIntSummaryStatisticsWithMethodReference()
{
   final Set<Movie> movies = Java8StreamsMoviesDemo.getMoviesSample();
   IntSummaryStatistics intSummaryStatistics =
      movies.stream().map(Movie::getImdbTopRating).collect(
         IntSummaryStatistics::new, IntSummaryStatistics::accept, IntSummaryStatistics::combine);
   out.println("IntSummaryStatistics on IMDB Top Rated Movies: " + intSummaryStatistics);
}

Когда приведенная выше демонстрация выполняется, ее вывод выглядит так:

1
IntSummaryStatistics on IMDB Top Rated Movies: IntSummaryStatistics{count=5, sum=106, min=1, average=21.200000, max=49}

До сих пор примеры демонстрировали использование классов SummaryStatistics в их наиболее распространенном случае использования (в сочетании с данными из потоков на основе существующих коллекций). В следующем примере демонстрируется, как можно создать экземпляр DoubleStream с нуля с помощью DoubleStream.Builder, а затем можно вызвать метод summaryStatistics () DoubleStream, чтобы получить экземпляр DoubleSummaryStatistics .

Получение экземпляра DoubleSummaryStatistics из DoubleStream

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Uses DoubleStream.builder to build an arbitrary DoubleStream.
 *
 * @return DoubleStream constructed with hard-coded doubles using
 *    a DoubleStream.builder.
 */
private static DoubleStream createSampleOfArbitraryDoubles()
{
   return DoubleStream.builder().add(12.4).add(13.6).add(9.7).add(24.5).add(10.2).add(3.0).build();
}
 
/**
 * Demonstrate use of an instance of DoubleSummaryStatistics
 * provided by DoubleStream.summaryStatistics().
 */
private static void demonstrateDoubleSummaryStatisticsOnDoubleStream()
{
   final DoubleSummaryStatistics doubleSummaryStatistics =
      createSampleOfArbitraryDoubles().summaryStatistics();
   out.println("'Arbitrary' Double Statistics: " + doubleSummaryStatistics);
}

Только что перечисленный код производит этот вывод:

1
'Arbitrary' Double Statistics: DoubleSummaryStatistics{count=6, sum=73.400000, min=3.000000, average=12.233333, max=24.500000}

Конечно, подобно только что показанному примеру, IntStream и IntStream.Builder могут предоставлять экземпляр IntSummaryStatistics а LongStream и LongStream.Builder могут предоставлять экземпляр LongSummaryStatistics .

Для использования классов SummaryStatistics не нужно иметь поток сбора данных или другой экземпляр BaseStream, поскольку они могут быть созданы непосредственно и использованы непосредственно для предопределенных числовых статистических операций. Следующий листинг кода демонстрирует это путем непосредственного создания экземпляра и последующего DoubleSummaryStatistics экземпляра DoubleSummaryStatistics .

Непосредственная реализация DoubleSummaryStatistics

01
02
03
04
05
06
07
08
09
10
11
12
13
14
/**
 * Demonstrate direct instantiation of and population of instance
 * of DoubleSummaryStatistics instance.
 */
private static void demonstrateDirectAccessToDoubleSummaryStatistics()
{
   final DoubleSummaryStatistics doubleSummaryStatistics =
      new DoubleSummaryStatistics();
   doubleSummaryStatistics.accept(5.0);
   doubleSummaryStatistics.accept(10.0);
   doubleSummaryStatistics.accept(15.0);
   doubleSummaryStatistics.accept(20.0);
   out.println("Direct DoubleSummaryStatistics Usage: " + doubleSummaryStatistics);
}

Результат выполнения предыдущего листинга кода показан ниже:

1
Direct DoubleSummaryStatistics Usage: DoubleSummaryStatistics{count=4, sum=50.000000, min=5.000000, average=12.500000, max=20.000000}

Как и в предыдущем листинге кода для DoubleSummaryStatistics , следующий листинг LongSummaryStatistics непосредственно создает экземпляр LongSummaryStatistics и заполняет его). Этот пример также демонстрирует, как классы SummaryStatistics предоставляют отдельные методы для запроса отдельной статистики.

Непосредственно создание LongSummaryStatistics / Запрос индивидуальной статистики

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * Demonstrate use of LongSummaryStatistics with this particular
 * example directly instantiating and populating an instance of
 * LongSummaryStatistics that represents hypothetical time
 * durations measured in milliseconds.
 */
private static void demonstrateLongSummaryStatistics()
{
   // This is a series of longs that might represent durations
   // of times such as might be calculated by subtracting the
   // value returned by System.currentTimeMillis() earlier in
   // code from the value returned by System.currentTimeMillis()
   // called later in the code.
   LongSummaryStatistics timeDurations = new LongSummaryStatistics();
   timeDurations.accept(5067054);
   timeDurations.accept(7064544);
   timeDurations.accept(5454544);
   timeDurations.accept(4455667);
   timeDurations.accept(9894450);
   timeDurations.accept(5555654);
   out.println("Test Results Analysis:");
   out.println("\tTotal Number of Tests: " + timeDurations.getCount());
   out.println("\tAverage Time Duration: " + timeDurations.getAverage());
   out.println("\tTotal Test Time: " + timeDurations.getSum());
   out.println("\tShortest Test Time: " + timeDurations.getMin());
   out.println("\tLongest Test Time: " + timeDurations.getMax());
}

Вывод из этого примера теперь показан:

1
2
3
4
5
6
Test Results Analysis:
 Total Number of Tests: 6
 Average Time Duration: 6248652.166666667
 Total Test Time: 37491913
 Shortest Test Time: 4455667
 Longest Test Time: 9894450

В большинстве примеров в этой статье я опирался на читаемые реализации toString () классов SummaryStatistics, чтобы продемонстрировать статистику, доступную в каждом классе. Этот последний пример, однако, продемонстрировал, что каждый отдельный тип статистики (количество значений, максимальное значение, минимальное значение, сумма значений и среднее значение) может быть извлечен индивидуально в числовой форме.

Вывод

Независимо от того, предоставляются ли анализируемые данные непосредственно в виде числового потока, предоставляются косвенно через поток коллекции или помещаются вручную в соответствующий экземпляр класса SummaryStatistics, три класса SummaryStatistics могут предоставлять полезные общие статистические вычисления для целых, длинных и двойных чисел.

Ссылка: Классы JDK 8 SummaryStatistics от нашего партнера JCG Дастина Маркса в блоге Inspired by Actual Events .