1 /*
2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package java.util;
26 
27 import java.util.function.DoubleConsumer;
28 import java.util.stream.Collector;
29 
30 /**
31  * A state object for collecting statistics such as count, min, max, sum, and
32  * average.
33  *
34  * <p>This class is designed to work with (though does not require)
35  * {@linkplain java.util.stream streams}. For example, you can compute
36  * summary statistics on a stream of doubles with:
37  * <pre> {@code
38  * DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new,
39  *                                                      DoubleSummaryStatistics::accept,
40  *                                                      DoubleSummaryStatistics::combine);
41  * }</pre>
42  *
43  * <p>{@code DoubleSummaryStatistics} can be used as a
44  * {@linkplain java.util.stream.Stream#collect(Collector) reduction}
45  * target for a {@linkplain java.util.stream.Stream stream}. For example:
46  *
47  * <pre> {@code
48  * DoubleSummaryStatistics stats = people.stream()
49  *     .collect(Collectors.summarizingDouble(Person::getWeight));
50  *}</pre>
51  *
52  * This computes, in a single pass, the count of people, as well as the minimum,
53  * maximum, sum, and average of their weights.
54  *
55  * @implNote This implementation is not thread safe. However, it is safe to use
56  * {@link java.util.stream.Collectors#summarizingDouble(java.util.function.ToDoubleFunction)
57  * Collectors.toDoubleStatistics()} on a parallel stream, because the parallel
58  * implementation of {@link java.util.stream.Stream#collect Stream.collect()}
59  * provides the necessary partitioning, isolation, and merging of results for
60  * safe and efficient parallel execution.
61  * @since 1.8
62  */
63 public class DoubleSummaryStatistics implements DoubleConsumer {
64     private long count;
65     private double sum;
66     private double sumCompensation; // Low order bits of sum
67     private double simpleSum; // Used to compute right sum for non-finite inputs
68     private double min = Double.POSITIVE_INFINITY;
69     private double max = Double.NEGATIVE_INFINITY;
70 
71     /**
72      * Construct an empty instance with zero count, zero sum,
73      * {@code Double.POSITIVE_INFINITY} min, {@code Double.NEGATIVE_INFINITY}
74      * max and zero average.
75      */
DoubleSummaryStatistics()76     public DoubleSummaryStatistics() { }
77 
78     /**
79      * Records another value into the summary information.
80      *
81      * @param value the input value
82      */
83     @Override
accept(double value)84     public void accept(double value) {
85         ++count;
86         simpleSum += value;
87         sumWithCompensation(value);
88         min = Math.min(min, value);
89         max = Math.max(max, value);
90     }
91 
92     /**
93      * Combines the state of another {@code DoubleSummaryStatistics} into this
94      * one.
95      *
96      * @param other another {@code DoubleSummaryStatistics}
97      * @throws NullPointerException if {@code other} is null
98      */
combine(DoubleSummaryStatistics other)99     public void combine(DoubleSummaryStatistics other) {
100         count += other.count;
101         simpleSum += other.simpleSum;
102         sumWithCompensation(other.sum);
103         sumWithCompensation(other.sumCompensation);
104         min = Math.min(min, other.min);
105         max = Math.max(max, other.max);
106     }
107 
108     /**
109      * Incorporate a new double value using Kahan summation /
110      * compensated summation.
111      */
sumWithCompensation(double value)112     private void sumWithCompensation(double value) {
113         double tmp = value - sumCompensation;
114         double velvel = sum + tmp; // Little wolf of rounding error
115         sumCompensation = (velvel - sum) - tmp;
116         sum = velvel;
117     }
118 
119     /**
120      * Return the count of values recorded.
121      *
122      * @return the count of values
123      */
getCount()124     public final long getCount() {
125         return count;
126     }
127 
128     /**
129      * Returns the sum of values recorded, or zero if no values have been
130      * recorded.
131      *
132      * If any recorded value is a NaN or the sum is at any point a NaN
133      * then the sum will be NaN.
134      *
135      * <p> The value of a floating-point sum is a function both of the
136      * input values as well as the order of addition operations. The
137      * order of addition operations of this method is intentionally
138      * not defined to allow for implementation flexibility to improve
139      * the speed and accuracy of the computed result.
140      *
141      * In particular, this method may be implemented using compensated
142      * summation or other technique to reduce the error bound in the
143      * numerical sum compared to a simple summation of {@code double}
144      * values.
145      *
146      * @apiNote Values sorted by increasing absolute magnitude tend to yield
147      * more accurate results.
148      *
149      * @return the sum of values, or zero if none
150      */
getSum()151     public final double getSum() {
152         // Better error bounds to add both terms as the final sum
153         double tmp =  sum + sumCompensation;
154         if (Double.isNaN(tmp) && Double.isInfinite(simpleSum))
155             // If the compensated sum is spuriously NaN from
156             // accumulating one or more same-signed infinite values,
157             // return the correctly-signed infinity stored in
158             // simpleSum.
159             return simpleSum;
160         else
161             return tmp;
162     }
163 
164     /**
165      * Returns the minimum recorded value, {@code Double.NaN} if any recorded
166      * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were
167      * recorded. Unlike the numerical comparison operators, this method
168      * considers negative zero to be strictly smaller than positive zero.
169      *
170      * @return the minimum recorded value, {@code Double.NaN} if any recorded
171      * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were
172      * recorded
173      */
getMin()174     public final double getMin() {
175         return min;
176     }
177 
178     /**
179      * Returns the maximum recorded value, {@code Double.NaN} if any recorded
180      * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were
181      * recorded. Unlike the numerical comparison operators, this method
182      * considers negative zero to be strictly smaller than positive zero.
183      *
184      * @return the maximum recorded value, {@code Double.NaN} if any recorded
185      * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were
186      * recorded
187      */
getMax()188     public final double getMax() {
189         return max;
190     }
191 
192     /**
193      * Returns the arithmetic mean of values recorded, or zero if no
194      * values have been recorded.
195      *
196      * If any recorded value is a NaN or the sum is at any point a NaN
197      * then the average will be code NaN.
198      *
199      * <p>The average returned can vary depending upon the order in
200      * which values are recorded.
201      *
202      * This method may be implemented using compensated summation or
203      * other technique to reduce the error bound in the {@link #getSum
204      * numerical sum} used to compute the average.
205      *
206      * @apiNote Values sorted by increasing absolute magnitude tend to yield
207      * more accurate results.
208      *
209      * @return the arithmetic mean of values, or zero if none
210      */
getAverage()211     public final double getAverage() {
212         return getCount() > 0 ? getSum() / getCount() : 0.0d;
213     }
214 
215     /**
216      * {@inheritDoc}
217      *
218      * Returns a non-empty string representation of this object suitable for
219      * debugging. The exact presentation format is unspecified and may vary
220      * between implementations and versions.
221      */
222     @Override
toString()223     public String toString() {
224         return String.format(
225             "%s{count=%d, sum=%f, min=%f, average=%f, max=%f}",
226             this.getClass().getSimpleName(),
227             getCount(),
228             getSum(),
229             getMin(),
230             getAverage(),
231             getMax());
232     }
233 }
234