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