From b01f07451e9a1d0ecd0f0a1e443d144432b90585 Mon Sep 17 00:00:00 2001 From: psandoz Date: Tue, 11 Apr 2017 17:25:09 -0400 Subject: [PATCH] 8178117: Add public state constructors for Int/Long/DoubleSummaryStatistics Reviewed-by: psandoz, bpb, briangoetz Contributed-by: Chris Dennis --- .../java/util/DoubleSummaryStatistics.java | 58 ++++++++++++++++++- .../java/util/IntSummaryStatistics.java | 49 +++++++++++++++- .../java/util/LongSummaryStatistics.java | 49 +++++++++++++++- .../CollectAndSummaryStatisticsTest.java | 47 ++++++++++++++- 4 files changed, 195 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java index 066c5d2d11..192a5bcb98 100644 --- a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java +++ b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.util; import java.util.function.DoubleConsumer; import java.util.stream.Collector; +import java.util.stream.DoubleStream; /** * A state object for collecting statistics such as count, min, max, sum, and @@ -69,12 +70,65 @@ public class DoubleSummaryStatistics implements DoubleConsumer { private double max = Double.NEGATIVE_INFINITY; /** - * Construct an empty instance with zero count, zero sum, + * Constructs an empty instance with zero count, zero sum, * {@code Double.POSITIVE_INFINITY} min, {@code Double.NEGATIVE_INFINITY} * max and zero average. */ public DoubleSummaryStatistics() { } + /** + * Constructs a non-empty instance with the specified {@code count}, + * {@code min}, {@code max}, and {@code sum}. + * + *

If {@code count} is zero then the remaining arguments are ignored and + * an empty instance is constructed. + * + *

If the arguments are inconsistent then an {@code IllegalArgumentException} + * is thrown. The necessary consistent argument conditions are: + *

+ * @apiNote + * The enforcement of argument correctness means that the retrieved set of + * recorded values obtained from a {@code DoubleSummaryStatistics} source + * instance may not be a legal set of arguments for this constructor due to + * arithmetic overflow of the source's recorded count of values. + * The consistent argument conditions are not sufficient to prevent the + * creation of an internally inconsistent instance. An example of such a + * state would be an instance with: {@code count} = 2, {@code min} = 1, + * {@code max} = 2, and {@code sum} = 0. + * + * @param count the count of values + * @param min the minimum value + * @param max the maximum value + * @param sum the sum of all values + * @throws IllegalArgumentException if the arguments are inconsistent + * @since 10 + */ + public DoubleSummaryStatistics(long count, double min, double max, double sum) + throws IllegalArgumentException { + if (count < 0L) { + throw new IllegalArgumentException("Negative count value"); + } else if (count > 0L) { + if (min > max) + throw new IllegalArgumentException("Minimum greater than maximum"); + + // All NaN or non NaN + var ncount = DoubleStream.of(min, max, sum).filter(Double::isNaN).count(); + if (ncount > 0 && ncount < 3) + throw new IllegalArgumentException("Some, not all, of the minimum, maximum, or sum is NaN"); + + this.count = count; + this.sum = sum; + this.simpleSum = sum; + this.sumCompensation = 0.0d; + this.min = min; + this.max = max; + } + // Use default field values if count == 0 + } + /** * Records another value into the summary information. * diff --git a/src/java.base/share/classes/java/util/IntSummaryStatistics.java b/src/java.base/share/classes/java/util/IntSummaryStatistics.java index f5691a8b67..9043c8a15c 100644 --- a/src/java.base/share/classes/java/util/IntSummaryStatistics.java +++ b/src/java.base/share/classes/java/util/IntSummaryStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,12 +69,57 @@ public class IntSummaryStatistics implements IntConsumer { private int max = Integer.MIN_VALUE; /** - * Construct an empty instance with zero count, zero sum, + * Constructs an empty instance with zero count, zero sum, * {@code Integer.MAX_VALUE} min, {@code Integer.MIN_VALUE} max and zero * average. */ public IntSummaryStatistics() { } + /** + * Constructs a non-empty instance with the specified {@code count}, + * {@code min}, {@code max}, and {@code sum}. + * + *

If {@code count} is zero then the remaining arguments are ignored and + * an empty instance is constructed. + * + *

If the arguments are inconsistent then an {@code IllegalArgumentException} + * is thrown. The necessary consistent argument conditions are: + *

+ * @apiNote + * The enforcement of argument correctness means that the retrieved set of + * recorded values obtained from a {@code IntSummaryStatistics} source + * instance may not be a legal set of arguments for this constructor due to + * arithmetic overflow of the source's recorded count of values. + * The consistent argument conditions are not sufficient to prevent the + * creation of an internally inconsistent instance. An example of such a + * state would be an instance with: {@code count} = 2, {@code min} = 1, + * {@code max} = 2, and {@code sum} = 0. + * + * @param count the count of values + * @param min the minimum value + * @param max the maximum value + * @param sum the sum of all values + * @throws IllegalArgumentException if the arguments are inconsistent + * @since 10 + */ + public IntSummaryStatistics(long count, int min, int max, long sum) + throws IllegalArgumentException { + if (count < 0L) { + throw new IllegalArgumentException("Negative count value"); + } else if (count > 0L) { + if (min > max) throw new IllegalArgumentException("Minimum greater than maximum"); + + this.count = count; + this.sum = sum; + this.min = min; + this.max = max; + } + // Use default field values if count == 0 + } + /** * Records a new value into the summary information * diff --git a/src/java.base/share/classes/java/util/LongSummaryStatistics.java b/src/java.base/share/classes/java/util/LongSummaryStatistics.java index 1ae091c110..06ad64795d 100644 --- a/src/java.base/share/classes/java/util/LongSummaryStatistics.java +++ b/src/java.base/share/classes/java/util/LongSummaryStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,12 +70,57 @@ public class LongSummaryStatistics implements LongConsumer, IntConsumer { private long max = Long.MIN_VALUE; /** - * Construct an empty instance with zero count, zero sum, + * Constructs an empty instance with zero count, zero sum, * {@code Long.MAX_VALUE} min, {@code Long.MIN_VALUE} max and zero * average. */ public LongSummaryStatistics() { } + /** + * Constructs a non-empty instance with the specified {@code count}, + * {@code min}, {@code max}, and {@code sum}. + * + *

If {@code count} is zero then the remaining arguments are ignored and + * an empty instance is constructed. + * + *

If the arguments are inconsistent then an {@code IllegalArgumentException} + * is thrown. The necessary consistent argument conditions are: + *

+ * @apiNote + * The enforcement of argument correctness means that the retrieved set of + * recorded values obtained from a {@code LongSummaryStatistics} source + * instance may not be a legal set of arguments for this constructor due to + * arithmetic overflow of the source's recorded count of values. + * The consistent argument conditions are not sufficient to prevent the + * creation of an internally inconsistent instance. An example of such a + * state would be an instance with: {@code count} = 2, {@code min} = 1, + * {@code max} = 2, and {@code sum} = 0. + * + * @param count the count of values + * @param min the minimum value + * @param max the maximum value + * @param sum the sum of all values + * @throws IllegalArgumentException if the arguments are inconsistent + * @since 10 + */ + public LongSummaryStatistics(long count, long min, long max, long sum) + throws IllegalArgumentException { + if (count < 0L) { + throw new IllegalArgumentException("Negative count value"); + } else if (count > 0L) { + if (min > max) throw new IllegalArgumentException("Minimum greater than maximum"); + + this.count = count; + this.sum = sum; + this.min = min; + this.max = max; + } + // Use default field values if count == 0 + } + /** * Records a new {@code int} value into the summary information. * diff --git a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/CollectAndSummaryStatisticsTest.java b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/CollectAndSummaryStatisticsTest.java index 22d91ba3f8..3b703eab7f 100644 --- a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/CollectAndSummaryStatisticsTest.java +++ b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/CollectAndSummaryStatisticsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /* * @test * @summary primtive stream collection with summary statistics - * @bug 8044047 + * @bug 8044047 8178117 */ package org.openjdk.tests.java.util.stream; @@ -91,11 +91,19 @@ public class CollectAndSummaryStatisticsTest extends OpTestCase { instances.add(countTo(1000).stream().mapToInt(i -> i).collect(IntSummaryStatistics::new, IntSummaryStatistics::accept, IntSummaryStatistics::combine)); + instances.add(countTo(1000).stream().mapToInt(i -> i).collect(() -> new IntSummaryStatistics(0, -1, 1001, 2), + IntSummaryStatistics::accept, + IntSummaryStatistics::combine)); instances.add(countTo(1000).parallelStream().collect(Collectors.summarizingInt(i -> i))); instances.add(countTo(1000).parallelStream().mapToInt(i -> i).summaryStatistics()); instances.add(countTo(1000).parallelStream().mapToInt(i -> i).collect(IntSummaryStatistics::new, IntSummaryStatistics::accept, IntSummaryStatistics::combine)); + instances.add(countTo(1000).parallelStream().mapToInt(i -> i).collect(() -> new IntSummaryStatistics(0, -1, 1001, 2), + IntSummaryStatistics::accept, + IntSummaryStatistics::combine)); + IntSummaryStatistics original = instances.get(0); + instances.add(new IntSummaryStatistics(original.getCount(), original.getMin(), original.getMax(), original.getSum())); for (IntSummaryStatistics stats : instances) { assertEquals(stats.getCount(), 1000); @@ -104,6 +112,9 @@ public class CollectAndSummaryStatisticsTest extends OpTestCase { assertEquals(stats.getMax(), 1000); assertEquals(stats.getMin(), 1); } + + expectThrows(IllegalArgumentException.class, () -> new IntSummaryStatistics(-1, 0, 0, 0)); + expectThrows(IllegalArgumentException.class, () -> new IntSummaryStatistics(1, 3, 2, 0)); } @@ -114,11 +125,19 @@ public class CollectAndSummaryStatisticsTest extends OpTestCase { instances.add(countTo(1000).stream().mapToLong(i -> i).collect(LongSummaryStatistics::new, LongSummaryStatistics::accept, LongSummaryStatistics::combine)); + instances.add(countTo(1000).stream().mapToInt(i -> i).collect(() -> new LongSummaryStatistics(0, -1, 1001, 2), + LongSummaryStatistics::accept, + LongSummaryStatistics::combine)); instances.add(countTo(1000).parallelStream().collect(Collectors.summarizingLong(i -> i))); instances.add(countTo(1000).parallelStream().mapToLong(i -> i).summaryStatistics()); instances.add(countTo(1000).parallelStream().mapToLong(i -> i).collect(LongSummaryStatistics::new, LongSummaryStatistics::accept, LongSummaryStatistics::combine)); + instances.add(countTo(1000).parallelStream().mapToInt(i -> i).collect(() -> new LongSummaryStatistics(0, -1, 1001, 2), + LongSummaryStatistics::accept, + LongSummaryStatistics::combine)); + LongSummaryStatistics original = instances.get(0); + instances.add(new LongSummaryStatistics(original.getCount(), original.getMin(), original.getMax(), original.getSum())); for (LongSummaryStatistics stats : instances) { assertEquals(stats.getCount(), 1000); @@ -127,6 +146,9 @@ public class CollectAndSummaryStatisticsTest extends OpTestCase { assertEquals(stats.getMax(), 1000L); assertEquals(stats.getMin(), 1L); } + + expectThrows(IllegalArgumentException.class, () -> new LongSummaryStatistics(-1, 0, 0, 0)); + expectThrows(IllegalArgumentException.class, () -> new LongSummaryStatistics(1, 3, 2, 0)); } public void testDoubleStatistics() { @@ -136,11 +158,19 @@ public class CollectAndSummaryStatisticsTest extends OpTestCase { instances.add(countTo(1000).stream().mapToDouble(i -> i).collect(DoubleSummaryStatistics::new, DoubleSummaryStatistics::accept, DoubleSummaryStatistics::combine)); + instances.add(countTo(1000).stream().mapToInt(i -> i).collect(() -> new DoubleSummaryStatistics(0, -1, 1001, 2), + DoubleSummaryStatistics::accept, + DoubleSummaryStatistics::combine)); instances.add(countTo(1000).parallelStream().collect(Collectors.summarizingDouble(i -> i))); instances.add(countTo(1000).parallelStream().mapToDouble(i -> i).summaryStatistics()); instances.add(countTo(1000).parallelStream().mapToDouble(i -> i).collect(DoubleSummaryStatistics::new, DoubleSummaryStatistics::accept, DoubleSummaryStatistics::combine)); + instances.add(countTo(1000).parallelStream().mapToInt(i -> i).collect(() -> new DoubleSummaryStatistics(0, -1, 1001, 2), + DoubleSummaryStatistics::accept, + DoubleSummaryStatistics::combine)); + DoubleSummaryStatistics original = instances.get(0); + instances.add(new DoubleSummaryStatistics(original.getCount(), original.getMin(), original.getMax(), original.getSum())); for (DoubleSummaryStatistics stats : instances) { assertEquals(stats.getCount(), 1000); @@ -149,5 +179,18 @@ public class CollectAndSummaryStatisticsTest extends OpTestCase { assertEquals(stats.getMax(), 1000.0); assertEquals(stats.getMin(), 1.0); } + + expectThrows(IllegalArgumentException.class, () -> new DoubleSummaryStatistics(-1, 0, 0, 0)); + expectThrows(IllegalArgumentException.class, () -> new DoubleSummaryStatistics(1, 3, 2, 0)); + double[] values = {1.0, Double.NaN}; + for (var min : values) { + for (var max : values) { + for (var sum : values) { + if (Double.isNaN(min) && Double.isNaN(max) && Double.isNaN(sum)) continue; + if (!Double.isNaN(min) && !Double.isNaN(max) && !Double.isNaN(sum)) continue; + expectThrows(IllegalArgumentException.class, () -> new DoubleSummaryStatistics(1, min, max, sum)); + } + } + } } } -- GitLab