001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2022, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ---------------------------------------- 028 * DefaultBoxAndWhiskerCategoryDataset.java 029 * ---------------------------------------- 030 * (C) Copyright 2003-2020, by David Browning and Contributors. 031 * 032 * Original Author: David Browning (for Australian Institute of Marine 033 * Science); 034 * Contributor(s): David Gilbert; 035 * 036 */ 037 038package org.jfree.data.statistics; 039 040import java.util.List; 041import java.util.Objects; 042 043import org.jfree.chart.api.PublicCloneable; 044 045import org.jfree.data.KeyedObjects2D; 046import org.jfree.data.Range; 047import org.jfree.data.RangeInfo; 048import org.jfree.data.general.AbstractDataset; 049import org.jfree.data.general.DatasetChangeEvent; 050 051/** 052 * A convenience class that provides a default implementation of the 053 * {@link BoxAndWhiskerCategoryDataset} interface. 054 */ 055public class DefaultBoxAndWhiskerCategoryDataset<R extends Comparable<R>, 056 C extends Comparable<C>> extends AbstractDataset 057 implements BoxAndWhiskerCategoryDataset<R, C>, RangeInfo, PublicCloneable { 058 059 /** Storage for the data. */ 060 protected KeyedObjects2D<R, C> data; 061 062 /** The minimum range value. */ 063 private double minimumRangeValue; 064 065 /** The row index for the cell that the minimum range value comes from. */ 066 private int minimumRangeValueRow; 067 068 /** 069 * The column index for the cell that the minimum range value comes from. 070 */ 071 private int minimumRangeValueColumn; 072 073 /** The maximum range value. */ 074 private double maximumRangeValue; 075 076 /** The row index for the cell that the maximum range value comes from. */ 077 private int maximumRangeValueRow; 078 079 /** 080 * The column index for the cell that the maximum range value comes from. 081 */ 082 private int maximumRangeValueColumn; 083 084 /** 085 * Creates a new dataset. 086 */ 087 public DefaultBoxAndWhiskerCategoryDataset() { 088 this.data = new KeyedObjects2D<>(); 089 this.minimumRangeValue = Double.NaN; 090 this.minimumRangeValueRow = -1; 091 this.minimumRangeValueColumn = -1; 092 this.maximumRangeValue = Double.NaN; 093 this.maximumRangeValueRow = -1; 094 this.maximumRangeValueColumn = -1; 095 } 096 097 /** 098 * Adds a list of values relating to one box-and-whisker entity to the 099 * table. The various median values are calculated. 100 * 101 * @param list a collection of values from which the various medians will 102 * be calculated. 103 * @param rowKey the row key ({@code null} not permitted). 104 * @param columnKey the column key ({@code null} not permitted). 105 * 106 * @see #add(BoxAndWhiskerItem, Comparable, Comparable) 107 */ 108 public void add(List<? extends Number> list, R rowKey, C columnKey) { 109 BoxAndWhiskerItem item = BoxAndWhiskerCalculator 110 .calculateBoxAndWhiskerStatistics(list); 111 add(item, rowKey, columnKey); 112 } 113 114 /** 115 * Adds a list of values relating to one Box and Whisker entity to the 116 * table. The various median values are calculated. 117 * 118 * @param item a box and whisker item ({@code null} not permitted). 119 * @param rowKey the row key ({@code null} not permitted). 120 * @param columnKey the column key ({@code null} not permitted). 121 * 122 * @see #add(List, Comparable, Comparable) 123 */ 124 public void add(BoxAndWhiskerItem item, R rowKey, C columnKey) { 125 126 this.data.addObject(item, rowKey, columnKey); 127 128 // update cached min and max values 129 int r = this.data.getRowIndex(rowKey); 130 int c = this.data.getColumnIndex(columnKey); 131 if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn 132 == c) || (this.minimumRangeValueRow == r 133 && this.minimumRangeValueColumn == c)) { 134 updateBounds(); 135 } 136 else { 137 138 double minval = Double.NaN; 139 if (item.getMinOutlier() != null) { 140 minval = item.getMinOutlier().doubleValue(); 141 } 142 double maxval = Double.NaN; 143 if (item.getMaxOutlier() != null) { 144 maxval = item.getMaxOutlier().doubleValue(); 145 } 146 147 if (Double.isNaN(this.maximumRangeValue)) { 148 this.maximumRangeValue = maxval; 149 this.maximumRangeValueRow = r; 150 this.maximumRangeValueColumn = c; 151 } 152 else if (maxval > this.maximumRangeValue) { 153 this.maximumRangeValue = maxval; 154 this.maximumRangeValueRow = r; 155 this.maximumRangeValueColumn = c; 156 } 157 158 if (Double.isNaN(this.minimumRangeValue)) { 159 this.minimumRangeValue = minval; 160 this.minimumRangeValueRow = r; 161 this.minimumRangeValueColumn = c; 162 } 163 else if (minval < this.minimumRangeValue) { 164 this.minimumRangeValue = minval; 165 this.minimumRangeValueRow = r; 166 this.minimumRangeValueColumn = c; 167 } 168 } 169 170 fireDatasetChanged(); 171 172 } 173 174 /** 175 * Removes an item from the dataset and sends a {@link DatasetChangeEvent} 176 * to all registered listeners. 177 * 178 * @param rowKey the row key ({@code null} not permitted). 179 * @param columnKey the column key ({@code null} not permitted). 180 * 181 * @see #add(BoxAndWhiskerItem, Comparable, Comparable) 182 * 183 * @since 1.0.7 184 */ 185 public void remove(R rowKey, C columnKey) { 186 // defer null argument checks 187 int r = getRowIndex(rowKey); 188 int c = getColumnIndex(columnKey); 189 this.data.removeObject(rowKey, columnKey); 190 191 // if this cell held a maximum and/or minimum value, we'll need to 192 // update the cached bounds... 193 if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn 194 == c) || (this.minimumRangeValueRow == r 195 && this.minimumRangeValueColumn == c)) { 196 updateBounds(); 197 } 198 199 fireDatasetChanged(); 200 } 201 202 /** 203 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 204 * to all registered listeners. 205 * 206 * @param rowIndex the row index. 207 * 208 * @see #removeColumn(int) 209 * 210 * @since 1.0.7 211 */ 212 public void removeRow(int rowIndex) { 213 this.data.removeRow(rowIndex); 214 updateBounds(); 215 fireDatasetChanged(); 216 } 217 218 /** 219 * Removes a row from the dataset and sends a {@link DatasetChangeEvent} 220 * to all registered listeners. 221 * 222 * @param rowKey the row key. 223 * 224 * @see #removeColumn(Comparable) 225 * 226 * @since 1.0.7 227 */ 228 public void removeRow(R rowKey) { 229 this.data.removeRow(rowKey); 230 updateBounds(); 231 fireDatasetChanged(); 232 } 233 234 /** 235 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 236 * to all registered listeners. 237 * 238 * @param columnIndex the column index. 239 * 240 * @see #removeRow(int) 241 * 242 * @since 1.0.7 243 */ 244 public void removeColumn(int columnIndex) { 245 this.data.removeColumn(columnIndex); 246 updateBounds(); 247 fireDatasetChanged(); 248 } 249 250 /** 251 * Removes a column from the dataset and sends a {@link DatasetChangeEvent} 252 * to all registered listeners. 253 * 254 * @param columnKey the column key. 255 * 256 * @see #removeRow(Comparable) 257 * 258 * @since 1.0.7 259 */ 260 public void removeColumn(C columnKey) { 261 this.data.removeColumn(columnKey); 262 updateBounds(); 263 fireDatasetChanged(); 264 } 265 266 /** 267 * Clears all data from the dataset and sends a {@link DatasetChangeEvent} 268 * to all registered listeners. 269 * 270 * @since 1.0.7 271 */ 272 public void clear() { 273 this.data.clear(); 274 updateBounds(); 275 fireDatasetChanged(); 276 } 277 278 /** 279 * Return an item from within the dataset. 280 * 281 * @param row the row index. 282 * @param column the column index. 283 * 284 * @return The item. 285 */ 286 public BoxAndWhiskerItem getItem(int row, int column) { 287 return (BoxAndWhiskerItem) this.data.getObject(row, column); 288 } 289 290 /** 291 * Returns the value for an item. 292 * 293 * @param row the row index. 294 * @param column the column index. 295 * 296 * @return The value. 297 * 298 * @see #getMedianValue(int, int) 299 * @see #getValue(Comparable, Comparable) 300 */ 301 @Override 302 public Number getValue(int row, int column) { 303 return getMedianValue(row, column); 304 } 305 306 /** 307 * Returns the value for an item. 308 * 309 * @param rowKey the row key. 310 * @param columnKey the columnKey. 311 * 312 * @return The value. 313 * 314 * @see #getMedianValue(Comparable, Comparable) 315 * @see #getValue(int, int) 316 */ 317 @Override 318 public Number getValue(R rowKey, C columnKey) { 319 return getMedianValue(rowKey, columnKey); 320 } 321 322 /** 323 * Returns the mean value for an item. 324 * 325 * @param row the row index (zero-based). 326 * @param column the column index (zero-based). 327 * 328 * @return The mean value. 329 * 330 * @see #getItem(int, int) 331 */ 332 @Override 333 public Number getMeanValue(int row, int column) { 334 Number result = null; 335 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, 336 column); 337 if (item != null) { 338 result = item.getMean(); 339 } 340 return result; 341 } 342 343 /** 344 * Returns the mean value for an item. 345 * 346 * @param rowKey the row key. 347 * @param columnKey the column key. 348 * 349 * @return The mean value. 350 * 351 * @see #getItem(int, int) 352 */ 353 @Override 354 public Number getMeanValue(R rowKey, C columnKey) { 355 Number result = null; 356 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 357 rowKey, columnKey); 358 if (item != null) { 359 result = item.getMean(); 360 } 361 return result; 362 } 363 364 /** 365 * Returns the median value for an item. 366 * 367 * @param row the row index (zero-based). 368 * @param column the column index (zero-based). 369 * 370 * @return The median value. 371 * 372 * @see #getItem(int, int) 373 */ 374 @Override 375 public Number getMedianValue(int row, int column) { 376 Number result = null; 377 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, 378 column); 379 if (item != null) { 380 result = item.getMedian(); 381 } 382 return result; 383 } 384 385 /** 386 * Returns the median value for an item. 387 * 388 * @param rowKey the row key. 389 * @param columnKey the columnKey. 390 * 391 * @return The median value. 392 * 393 * @see #getItem(int, int) 394 */ 395 @Override 396 public Number getMedianValue(R rowKey, C columnKey) { 397 Number result = null; 398 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 399 rowKey, columnKey); 400 if (item != null) { 401 result = item.getMedian(); 402 } 403 return result; 404 } 405 406 /** 407 * Returns the first quartile value. 408 * 409 * @param row the row index (zero-based). 410 * @param column the column index (zero-based). 411 * 412 * @return The first quartile value. 413 * 414 * @see #getItem(int, int) 415 */ 416 @Override 417 public Number getQ1Value(int row, int column) { 418 Number result = null; 419 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 420 row, column); 421 if (item != null) { 422 result = item.getQ1(); 423 } 424 return result; 425 } 426 427 /** 428 * Returns the first quartile value. 429 * 430 * @param rowKey the row key. 431 * @param columnKey the column key. 432 * 433 * @return The first quartile value. 434 * 435 * @see #getItem(int, int) 436 */ 437 @Override 438 public Number getQ1Value(R rowKey, C columnKey) { 439 Number result = null; 440 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 441 rowKey, columnKey); 442 if (item != null) { 443 result = item.getQ1(); 444 } 445 return result; 446 } 447 448 /** 449 * Returns the third quartile value. 450 * 451 * @param row the row index (zero-based). 452 * @param column the column index (zero-based). 453 * 454 * @return The third quartile value. 455 * 456 * @see #getItem(int, int) 457 */ 458 @Override 459 public Number getQ3Value(int row, int column) { 460 Number result = null; 461 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 462 row, column); 463 if (item != null) { 464 result = item.getQ3(); 465 } 466 return result; 467 } 468 469 /** 470 * Returns the third quartile value. 471 * 472 * @param rowKey the row key. 473 * @param columnKey the column key. 474 * 475 * @return The third quartile value. 476 * 477 * @see #getItem(int, int) 478 */ 479 @Override 480 public Number getQ3Value(R rowKey, C columnKey) { 481 Number result = null; 482 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 483 rowKey, columnKey); 484 if (item != null) { 485 result = item.getQ3(); 486 } 487 return result; 488 } 489 490 /** 491 * Returns the column index for a given key. 492 * 493 * @param key the column key ({@code null} not permitted). 494 * 495 * @return The column index. 496 * 497 * @see #getColumnKey(int) 498 */ 499 @Override 500 public int getColumnIndex(C key) { 501 return this.data.getColumnIndex(key); 502 } 503 504 /** 505 * Returns a column key. 506 * 507 * @param column the column index (zero-based). 508 * 509 * @return The column key. 510 * 511 * @see #getColumnIndex(Comparable) 512 */ 513 @Override 514 public C getColumnKey(int column) { 515 return this.data.getColumnKey(column); 516 } 517 518 /** 519 * Returns the column keys. 520 * 521 * @return The keys. 522 * 523 * @see #getRowKeys() 524 */ 525 @Override 526 public List<C> getColumnKeys() { 527 return this.data.getColumnKeys(); 528 } 529 530 /** 531 * Returns the row index for a given key. 532 * 533 * @param key the row key ({@code null} not permitted). 534 * 535 * @return The row index. 536 * 537 * @see #getRowKey(int) 538 */ 539 @Override 540 public int getRowIndex(R key) { 541 // defer null argument check 542 return this.data.getRowIndex(key); 543 } 544 545 /** 546 * Returns a row key. 547 * 548 * @param row the row index (zero-based). 549 * 550 * @return The row key. 551 * 552 * @see #getRowIndex(Comparable) 553 */ 554 @Override 555 public R getRowKey(int row) { 556 return this.data.getRowKey(row); 557 } 558 559 /** 560 * Returns the row keys. 561 * 562 * @return The keys. 563 * 564 * @see #getColumnKeys() 565 */ 566 @Override 567 public List<R> getRowKeys() { 568 return this.data.getRowKeys(); 569 } 570 571 /** 572 * Returns the number of rows in the table. 573 * 574 * @return The row count. 575 * 576 * @see #getColumnCount() 577 */ 578 @Override 579 public int getRowCount() { 580 return this.data.getRowCount(); 581 } 582 583 /** 584 * Returns the number of columns in the table. 585 * 586 * @return The column count. 587 * 588 * @see #getRowCount() 589 */ 590 @Override 591 public int getColumnCount() { 592 return this.data.getColumnCount(); 593 } 594 595 /** 596 * Returns the minimum y-value in the dataset. 597 * 598 * @param includeInterval a flag that determines whether or not the 599 * y-interval is taken into account. 600 * 601 * @return The minimum value. 602 * 603 * @see #getRangeUpperBound(boolean) 604 */ 605 @Override 606 public double getRangeLowerBound(boolean includeInterval) { 607 return this.minimumRangeValue; 608 } 609 610 /** 611 * Returns the maximum y-value in the dataset. 612 * 613 * @param includeInterval a flag that determines whether or not the 614 * y-interval is taken into account. 615 * 616 * @return The maximum value. 617 * 618 * @see #getRangeLowerBound(boolean) 619 */ 620 @Override 621 public double getRangeUpperBound(boolean includeInterval) { 622 return this.maximumRangeValue; 623 } 624 625 /** 626 * Returns the range of the values in this dataset's range. 627 * 628 * @param includeInterval a flag that determines whether or not the 629 * y-interval is taken into account. 630 * 631 * @return The range. 632 */ 633 @Override 634 public Range getRangeBounds(boolean includeInterval) { 635 return new Range(this.minimumRangeValue, this.maximumRangeValue); 636 } 637 638 /** 639 * Returns the minimum regular (non outlier) value for an item. 640 * 641 * @param row the row index (zero-based). 642 * @param column the column index (zero-based). 643 * 644 * @return The minimum regular value. 645 * 646 * @see #getItem(int, int) 647 */ 648 @Override 649 public Number getMinRegularValue(int row, int column) { 650 Number result = null; 651 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 652 row, column); 653 if (item != null) { 654 result = item.getMinRegularValue(); 655 } 656 return result; 657 } 658 659 /** 660 * Returns the minimum regular (non outlier) value for an item. 661 * 662 * @param rowKey the row key. 663 * @param columnKey the column key. 664 * 665 * @return The minimum regular value. 666 * 667 * @see #getItem(int, int) 668 */ 669 @Override 670 public Number getMinRegularValue(R rowKey, C columnKey) { 671 Number result = null; 672 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 673 rowKey, columnKey); 674 if (item != null) { 675 result = item.getMinRegularValue(); 676 } 677 return result; 678 } 679 680 /** 681 * Returns the maximum regular (non outlier) value for an item. 682 * 683 * @param row the row index (zero-based). 684 * @param column the column index (zero-based). 685 * 686 * @return The maximum regular value. 687 * 688 * @see #getItem(int, int) 689 */ 690 @Override 691 public Number getMaxRegularValue(int row, int column) { 692 Number result = null; 693 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 694 row, column); 695 if (item != null) { 696 result = item.getMaxRegularValue(); 697 } 698 return result; 699 } 700 701 /** 702 * Returns the maximum regular (non outlier) value for an item. 703 * 704 * @param rowKey the row key. 705 * @param columnKey the column key. 706 * 707 * @return The maximum regular value. 708 * 709 * @see #getItem(int, int) 710 */ 711 @Override 712 public Number getMaxRegularValue(R rowKey, C columnKey) { 713 Number result = null; 714 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 715 rowKey, columnKey); 716 if (item != null) { 717 result = item.getMaxRegularValue(); 718 } 719 return result; 720 } 721 722 /** 723 * Returns the minimum outlier (non farout) value for an item. 724 * 725 * @param row the row index (zero-based). 726 * @param column the column index (zero-based). 727 * 728 * @return The minimum outlier. 729 * 730 * @see #getItem(int, int) 731 */ 732 @Override 733 public Number getMinOutlier(int row, int column) { 734 Number result = null; 735 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 736 row, column); 737 if (item != null) { 738 result = item.getMinOutlier(); 739 } 740 return result; 741 } 742 743 /** 744 * Returns the minimum outlier (non farout) value for an item. 745 * 746 * @param rowKey the row key. 747 * @param columnKey the column key. 748 * 749 * @return The minimum outlier. 750 * 751 * @see #getItem(int, int) 752 */ 753 @Override 754 public Number getMinOutlier(R rowKey, C columnKey) { 755 Number result = null; 756 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 757 rowKey, columnKey); 758 if (item != null) { 759 result = item.getMinOutlier(); 760 } 761 return result; 762 } 763 764 /** 765 * Returns the maximum outlier (non farout) value for an item. 766 * 767 * @param row the row index (zero-based). 768 * @param column the column index (zero-based). 769 * 770 * @return The maximum outlier. 771 * 772 * @see #getItem(int, int) 773 */ 774 @Override 775 public Number getMaxOutlier(int row, int column) { 776 Number result = null; 777 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 778 row, column); 779 if (item != null) { 780 result = item.getMaxOutlier(); 781 } 782 return result; 783 } 784 785 /** 786 * Returns the maximum outlier (non farout) value for an item. 787 * 788 * @param rowKey the row key. 789 * @param columnKey the column key. 790 * 791 * @return The maximum outlier. 792 * 793 * @see #getItem(int, int) 794 */ 795 @Override 796 public Number getMaxOutlier(R rowKey, C columnKey) { 797 Number result = null; 798 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 799 rowKey, columnKey); 800 if (item != null) { 801 result = item.getMaxOutlier(); 802 } 803 return result; 804 } 805 806 /** 807 * Returns a list of outlier values for an item. 808 * 809 * @param row the row index (zero-based). 810 * @param column the column index (zero-based). 811 * 812 * @return A list of outlier values. 813 * 814 * @see #getItem(int, int) 815 */ 816 @Override 817 public List<? extends Number> getOutliers(int row, int column) { 818 List result = null; 819 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 820 row, column); 821 if (item != null) { 822 result = item.getOutliers(); 823 } 824 return result; 825 } 826 827 /** 828 * Returns a list of outlier values for an item. 829 * 830 * @param rowKey the row key. 831 * @param columnKey the column key. 832 * 833 * @return A list of outlier values. 834 * 835 * @see #getItem(int, int) 836 */ 837 @Override 838 public List<? extends Number> getOutliers(R rowKey, C columnKey) { 839 List result = null; 840 BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( 841 rowKey, columnKey); 842 if (item != null) { 843 result = item.getOutliers(); 844 } 845 return result; 846 } 847 848 /** 849 * Resets the cached bounds, by iterating over the entire dataset to find 850 * the current bounds. 851 */ 852 private void updateBounds() { 853 this.minimumRangeValue = Double.NaN; 854 this.minimumRangeValueRow = -1; 855 this.minimumRangeValueColumn = -1; 856 this.maximumRangeValue = Double.NaN; 857 this.maximumRangeValueRow = -1; 858 this.maximumRangeValueColumn = -1; 859 int rowCount = getRowCount(); 860 int columnCount = getColumnCount(); 861 for (int r = 0; r < rowCount; r++) { 862 for (int c = 0; c < columnCount; c++) { 863 BoxAndWhiskerItem item = getItem(r, c); 864 if (item != null) { 865 Number min = item.getMinOutlier(); 866 if (min != null) { 867 double minv = min.doubleValue(); 868 if (!Double.isNaN(minv)) { 869 if (minv < this.minimumRangeValue || Double.isNaN( 870 this.minimumRangeValue)) { 871 this.minimumRangeValue = minv; 872 this.minimumRangeValueRow = r; 873 this.minimumRangeValueColumn = c; 874 } 875 } 876 } 877 Number max = item.getMaxOutlier(); 878 if (max != null) { 879 double maxv = max.doubleValue(); 880 if (!Double.isNaN(maxv)) { 881 if (maxv > this.maximumRangeValue || Double.isNaN( 882 this.maximumRangeValue)) { 883 this.maximumRangeValue = maxv; 884 this.maximumRangeValueRow = r; 885 this.maximumRangeValueColumn = c; 886 } 887 } 888 } 889 } 890 } 891 } 892 } 893 894 /** 895 * Tests this dataset for equality with an arbitrary object. 896 * 897 * @param obj the object to test against ({@code null} permitted). 898 * 899 * @return A boolean. 900 */ 901 @Override 902 public boolean equals(Object obj) { 903 if (obj == this) { 904 return true; 905 } 906 if (obj instanceof DefaultBoxAndWhiskerCategoryDataset) { 907 DefaultBoxAndWhiskerCategoryDataset dataset 908 = (DefaultBoxAndWhiskerCategoryDataset) obj; 909 return Objects.equals(this.data, dataset.data); 910 } 911 return false; 912 } 913 914 @Override 915 public int hashCode() { 916 int hash = 5; 917 hash = 23 * hash + Objects.hashCode( this.data ); 918 return hash; 919 } 920 921 /** 922 * Returns a clone of this dataset. 923 * 924 * @return A clone. 925 * 926 * @throws CloneNotSupportedException if cloning is not possible. 927 */ 928 @Override 929 public Object clone() throws CloneNotSupportedException { 930 DefaultBoxAndWhiskerCategoryDataset<R, C> clone 931 = (DefaultBoxAndWhiskerCategoryDataset) super.clone(); 932 clone.data = (KeyedObjects2D<R, C>) this.data.clone(); 933 return clone; 934 } 935 936}