001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math4.legacy.linear; 018 019import java.io.Serializable; 020 021import org.apache.commons.math4.legacy.exception.DimensionMismatchException; 022import org.apache.commons.math4.legacy.exception.MathArithmeticException; 023import org.apache.commons.math4.legacy.exception.NotPositiveException; 024import org.apache.commons.math4.legacy.exception.OutOfRangeException; 025import org.apache.commons.math4.legacy.exception.util.LocalizedFormats; 026import org.apache.commons.math4.core.jdkmath.JdkMath; 027import org.apache.commons.math4.legacy.linear.OpenIntToDoubleHashMap.Iterator; 028 029/** 030 * This class implements the {@link RealVector} interface with a 031 * {@link OpenIntToDoubleHashMap} backing store. 032 * <p> 033 * Caveat: This implementation assumes that, for any {@code x}, 034 * the equality {@code x * 0d == 0d} holds. But it is is not true for 035 * {@code NaN}. Moreover, zero entries will lose their sign. 036 * Some operations (that involve {@code NaN} and/or infinities) may 037 * thus give incorrect results, like multiplications, divisions or 038 * functions mapping. 039 * </p> 040 * @since 2.0 041 */ 042public class OpenMapRealVector extends SparseRealVector 043 implements Serializable { 044 /** Default Tolerance for having a value considered zero. */ 045 public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12; 046 /** Serializable version identifier. */ 047 private static final long serialVersionUID = 8772222695580707260L; 048 /** Entries of the vector. */ 049 private final OpenIntToDoubleHashMap entries; 050 /** Dimension of the vector. */ 051 private final int virtualSize; 052 /** Tolerance for having a value considered zero. */ 053 private final double epsilon; 054 055 /** 056 * Build a 0-length vector. 057 * Zero-length vectors may be used to initialized construction of vectors 058 * by data gathering. We start with zero-length and use either the {@link 059 * #OpenMapRealVector(OpenMapRealVector, int)} constructor 060 * or one of the {@code append} method ({@link #append(double)}, 061 * {@link #append(RealVector)}) to gather data into this vector. 062 */ 063 public OpenMapRealVector() { 064 this(0, DEFAULT_ZERO_TOLERANCE); 065 } 066 067 /** 068 * Construct a vector of zeroes. 069 * 070 * @param dimension Size of the vector. 071 */ 072 public OpenMapRealVector(int dimension) { 073 this(dimension, DEFAULT_ZERO_TOLERANCE); 074 } 075 076 /** 077 * Construct a vector of zeroes, specifying zero tolerance. 078 * 079 * @param dimension Size of the vector. 080 * @param epsilon Tolerance below which a value considered zero. 081 */ 082 public OpenMapRealVector(int dimension, double epsilon) { 083 virtualSize = dimension; 084 entries = new OpenIntToDoubleHashMap(0.0); 085 this.epsilon = epsilon; 086 } 087 088 /** 089 * Build a resized vector, for use with append. 090 * 091 * @param v Original vector. 092 * @param resize Amount to add. 093 */ 094 protected OpenMapRealVector(OpenMapRealVector v, int resize) { 095 virtualSize = v.getDimension() + resize; 096 entries = new OpenIntToDoubleHashMap(v.entries); 097 epsilon = v.epsilon; 098 } 099 100 /** 101 * Build a vector with known the sparseness (for advanced use only). 102 * 103 * @param dimension Size of the vector. 104 * @param expectedSize The expected number of non-zero entries. 105 */ 106 public OpenMapRealVector(int dimension, int expectedSize) { 107 this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE); 108 } 109 110 /** 111 * Build a vector with known the sparseness and zero tolerance 112 * setting (for advanced use only). 113 * 114 * @param dimension Size of the vector. 115 * @param expectedSize Expected number of non-zero entries. 116 * @param epsilon Tolerance below which a value is considered zero. 117 */ 118 public OpenMapRealVector(int dimension, int expectedSize, double epsilon) { 119 virtualSize = dimension; 120 entries = new OpenIntToDoubleHashMap(expectedSize, 0.0); 121 this.epsilon = epsilon; 122 } 123 124 /** 125 * Create from an array. 126 * Only non-zero entries will be stored. 127 * 128 * @param values Set of values to create from. 129 */ 130 public OpenMapRealVector(double[] values) { 131 this(values, DEFAULT_ZERO_TOLERANCE); 132 } 133 134 /** 135 * Create from an array, specifying zero tolerance. 136 * Only non-zero entries will be stored. 137 * 138 * @param values Set of values to create from. 139 * @param epsilon Tolerance below which a value is considered zero. 140 */ 141 public OpenMapRealVector(double[] values, double epsilon) { 142 virtualSize = values.length; 143 entries = new OpenIntToDoubleHashMap(0.0); 144 this.epsilon = epsilon; 145 for (int key = 0; key < values.length; key++) { 146 double value = values[key]; 147 if (!isDefaultValue(value)) { 148 entries.put(key, value); 149 } 150 } 151 } 152 153 /** 154 * Create from an array. 155 * Only non-zero entries will be stored. 156 * 157 * @param values The set of values to create from 158 */ 159 public OpenMapRealVector(Double[] values) { 160 this(values, DEFAULT_ZERO_TOLERANCE); 161 } 162 163 /** 164 * Create from an array. 165 * Only non-zero entries will be stored. 166 * 167 * @param values Set of values to create from. 168 * @param epsilon Tolerance below which a value is considered zero. 169 */ 170 public OpenMapRealVector(Double[] values, double epsilon) { 171 virtualSize = values.length; 172 entries = new OpenIntToDoubleHashMap(0.0); 173 this.epsilon = epsilon; 174 for (int key = 0; key < values.length; key++) { 175 double value = values[key].doubleValue(); 176 if (!isDefaultValue(value)) { 177 entries.put(key, value); 178 } 179 } 180 } 181 182 /** 183 * Copy constructor. 184 * 185 * @param v Instance to copy from. 186 */ 187 public OpenMapRealVector(OpenMapRealVector v) { 188 virtualSize = v.getDimension(); 189 entries = new OpenIntToDoubleHashMap(v.getEntries()); 190 epsilon = v.epsilon; 191 } 192 193 /** 194 * Generic copy constructor. 195 * 196 * @param v Instance to copy from. 197 */ 198 public OpenMapRealVector(RealVector v) { 199 virtualSize = v.getDimension(); 200 entries = new OpenIntToDoubleHashMap(0.0); 201 epsilon = DEFAULT_ZERO_TOLERANCE; 202 for (int key = 0; key < virtualSize; key++) { 203 double value = v.getEntry(key); 204 if (!isDefaultValue(value)) { 205 entries.put(key, value); 206 } 207 } 208 } 209 210 /** 211 * Get the entries of this instance. 212 * 213 * @return the entries of this instance. 214 */ 215 private OpenIntToDoubleHashMap getEntries() { 216 return entries; 217 } 218 219 /** 220 * Determine if this value is within epsilon of zero. 221 * 222 * @param value Value to test 223 * @return {@code true} if this value is within epsilon to zero, 224 * {@code false} otherwise. 225 * @since 2.1 226 */ 227 protected boolean isDefaultValue(double value) { 228 return JdkMath.abs(value) < epsilon; 229 } 230 231 /** {@inheritDoc} */ 232 @Override 233 public RealVector add(RealVector v) 234 throws DimensionMismatchException { 235 checkVectorDimensions(v.getDimension()); 236 if (v instanceof OpenMapRealVector) { 237 return add((OpenMapRealVector) v); 238 } else { 239 return super.add(v); 240 } 241 } 242 243 /** 244 * Optimized method to add two OpenMapRealVectors. 245 * It copies the larger vector, then iterates over the smaller. 246 * 247 * @param v Vector to add. 248 * @return the sum of {@code this} and {@code v}. 249 * @throws DimensionMismatchException if the dimensions do not match. 250 */ 251 public OpenMapRealVector add(OpenMapRealVector v) 252 throws DimensionMismatchException { 253 checkVectorDimensions(v.getDimension()); 254 boolean copyThis = entries.size() > v.entries.size(); 255 OpenMapRealVector res = copyThis ? this.copy() : v.copy(); 256 Iterator iter = copyThis ? v.entries.iterator() : entries.iterator(); 257 OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries; 258 while (iter.hasNext()) { 259 iter.advance(); 260 int key = iter.key(); 261 if (randomAccess.containsKey(key)) { 262 res.setEntry(key, randomAccess.get(key) + iter.value()); 263 } else { 264 res.setEntry(key, iter.value()); 265 } 266 } 267 return res; 268 } 269 270 /** 271 * Optimized method to append a OpenMapRealVector. 272 * @param v vector to append 273 * @return The result of appending {@code v} to self 274 */ 275 public OpenMapRealVector append(OpenMapRealVector v) { 276 OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension()); 277 Iterator iter = v.entries.iterator(); 278 while (iter.hasNext()) { 279 iter.advance(); 280 res.setEntry(iter.key() + virtualSize, iter.value()); 281 } 282 return res; 283 } 284 285 /** {@inheritDoc} */ 286 @Override 287 public OpenMapRealVector append(RealVector v) { 288 if (v instanceof OpenMapRealVector) { 289 return append((OpenMapRealVector) v); 290 } else { 291 final OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension()); 292 for (int i = 0; i < v.getDimension(); i++) { 293 res.setEntry(i + virtualSize, v.getEntry(i)); 294 } 295 return res; 296 } 297 } 298 299 /** {@inheritDoc} */ 300 @Override 301 public OpenMapRealVector append(double d) { 302 OpenMapRealVector res = new OpenMapRealVector(this, 1); 303 res.setEntry(virtualSize, d); 304 return res; 305 } 306 307 /** 308 * {@inheritDoc} 309 * @since 2.1 310 */ 311 @Override 312 public OpenMapRealVector copy() { 313 return new OpenMapRealVector(this); 314 } 315 316 /** {@inheritDoc} */ 317 @Override 318 public OpenMapRealVector ebeDivide(RealVector v) 319 throws DimensionMismatchException { 320 checkVectorDimensions(v.getDimension()); 321 OpenMapRealVector res = new OpenMapRealVector(this); 322 /* 323 * MATH-803: it is not sufficient to loop through non zero entries of 324 * this only. Indeed, if this[i] = 0d and v[i] = 0d, then 325 * this[i] / v[i] = NaN, and not 0d. 326 */ 327 final int n = getDimension(); 328 for (int i = 0; i < n; i++) { 329 res.setEntry(i, this.getEntry(i) / v.getEntry(i)); 330 } 331 return res; 332 } 333 334 /** {@inheritDoc} */ 335 @Override 336 public OpenMapRealVector ebeMultiply(RealVector v) 337 throws DimensionMismatchException { 338 checkVectorDimensions(v.getDimension()); 339 OpenMapRealVector res = new OpenMapRealVector(this); 340 Iterator iter = entries.iterator(); 341 while (iter.hasNext()) { 342 iter.advance(); 343 res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key())); 344 } 345 return res; 346 } 347 348 /** {@inheritDoc} */ 349 @Override 350 public OpenMapRealVector getSubVector(int index, int n) 351 throws NotPositiveException, OutOfRangeException { 352 checkIndex(index); 353 if (n < 0) { 354 throw new NotPositiveException(LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n); 355 } 356 checkIndex(index + n - 1); 357 OpenMapRealVector res = new OpenMapRealVector(n); 358 int end = index + n; 359 Iterator iter = entries.iterator(); 360 while (iter.hasNext()) { 361 iter.advance(); 362 int key = iter.key(); 363 if (key >= index && key < end) { 364 res.setEntry(key - index, iter.value()); 365 } 366 } 367 return res; 368 } 369 370 /** {@inheritDoc} */ 371 @Override 372 public int getDimension() { 373 return virtualSize; 374 } 375 376 /** 377 * Optimized method to compute distance. 378 * 379 * @param v Vector to compute distance to. 380 * @return the distance from {@code this} and {@code v}. 381 * @throws DimensionMismatchException if the dimensions do not match. 382 */ 383 public double getDistance(OpenMapRealVector v) 384 throws DimensionMismatchException { 385 checkVectorDimensions(v.getDimension()); 386 Iterator iter = entries.iterator(); 387 double res = 0; 388 while (iter.hasNext()) { 389 iter.advance(); 390 int key = iter.key(); 391 double delta; 392 delta = iter.value() - v.getEntry(key); 393 res += delta * delta; 394 } 395 iter = v.getEntries().iterator(); 396 while (iter.hasNext()) { 397 iter.advance(); 398 int key = iter.key(); 399 if (!entries.containsKey(key)) { 400 final double value = iter.value(); 401 res += value * value; 402 } 403 } 404 return JdkMath.sqrt(res); 405 } 406 407 /** {@inheritDoc} */ 408 @Override 409 public double getDistance(RealVector v) throws DimensionMismatchException { 410 checkVectorDimensions(v.getDimension()); 411 if (v instanceof OpenMapRealVector) { 412 return getDistance((OpenMapRealVector) v); 413 } else { 414 return super.getDistance(v); 415 } 416 } 417 418 /** {@inheritDoc} */ 419 @Override 420 public double getEntry(int index) throws OutOfRangeException { 421 checkIndex(index); 422 return entries.get(index); 423 } 424 425 /** 426 * Distance between two vectors. 427 * This method computes the distance consistent with 428 * L<sub>1</sub> norm, i.e. the sum of the absolute values of 429 * elements differences. 430 * 431 * @param v Vector to which distance is requested. 432 * @return distance between this vector and {@code v}. 433 * @throws DimensionMismatchException if the dimensions do not match. 434 */ 435 public double getL1Distance(OpenMapRealVector v) 436 throws DimensionMismatchException { 437 checkVectorDimensions(v.getDimension()); 438 double max = 0; 439 Iterator iter = entries.iterator(); 440 while (iter.hasNext()) { 441 iter.advance(); 442 double delta = JdkMath.abs(iter.value() - v.getEntry(iter.key())); 443 max += delta; 444 } 445 iter = v.getEntries().iterator(); 446 while (iter.hasNext()) { 447 iter.advance(); 448 int key = iter.key(); 449 if (!entries.containsKey(key)) { 450 double delta = JdkMath.abs(iter.value()); 451 max += JdkMath.abs(delta); 452 } 453 } 454 return max; 455 } 456 457 /** {@inheritDoc} */ 458 @Override 459 public double getL1Distance(RealVector v) 460 throws DimensionMismatchException { 461 checkVectorDimensions(v.getDimension()); 462 if (v instanceof OpenMapRealVector) { 463 return getL1Distance((OpenMapRealVector) v); 464 } else { 465 return super.getL1Distance(v); 466 } 467 } 468 469 /** 470 * Optimized method to compute LInfDistance. 471 * 472 * @param v Vector to compute distance from. 473 * @return the LInfDistance. 474 * @throws DimensionMismatchException if the dimensions do not match. 475 */ 476 private double getLInfDistance(OpenMapRealVector v) 477 throws DimensionMismatchException { 478 checkVectorDimensions(v.getDimension()); 479 double max = 0; 480 Iterator iter = entries.iterator(); 481 while (iter.hasNext()) { 482 iter.advance(); 483 double delta = JdkMath.abs(iter.value() - v.getEntry(iter.key())); 484 if (delta > max) { 485 max = delta; 486 } 487 } 488 iter = v.getEntries().iterator(); 489 while (iter.hasNext()) { 490 iter.advance(); 491 int key = iter.key(); 492 if (!entries.containsKey(key) && iter.value() > max) { 493 max = iter.value(); 494 } 495 } 496 return max; 497 } 498 499 /** {@inheritDoc} */ 500 @Override 501 public double getLInfDistance(RealVector v) 502 throws DimensionMismatchException { 503 checkVectorDimensions(v.getDimension()); 504 if (v instanceof OpenMapRealVector) { 505 return getLInfDistance((OpenMapRealVector) v); 506 } else { 507 return super.getLInfDistance(v); 508 } 509 } 510 511 /** {@inheritDoc} */ 512 @Override 513 public boolean isInfinite() { 514 boolean infiniteFound = false; 515 Iterator iter = entries.iterator(); 516 while (iter.hasNext()) { 517 iter.advance(); 518 final double value = iter.value(); 519 if (Double.isNaN(value)) { 520 return false; 521 } 522 if (Double.isInfinite(value)) { 523 infiniteFound = true; 524 } 525 } 526 return infiniteFound; 527 } 528 529 /** {@inheritDoc} */ 530 @Override 531 public boolean isNaN() { 532 Iterator iter = entries.iterator(); 533 while (iter.hasNext()) { 534 iter.advance(); 535 if (Double.isNaN(iter.value())) { 536 return true; 537 } 538 } 539 return false; 540 } 541 542 /** {@inheritDoc} */ 543 @Override 544 public OpenMapRealVector mapAdd(double d) { 545 return copy().mapAddToSelf(d); 546 } 547 548 /** {@inheritDoc} */ 549 @Override 550 public OpenMapRealVector mapAddToSelf(double d) { 551 for (int i = 0; i < virtualSize; i++) { 552 setEntry(i, getEntry(i) + d); 553 } 554 return this; 555 } 556 557 /** {@inheritDoc} */ 558 @Override 559 public void setEntry(int index, double value) 560 throws OutOfRangeException { 561 checkIndex(index); 562 if (!isDefaultValue(value)) { 563 entries.put(index, value); 564 } else if (entries.containsKey(index)) { 565 entries.remove(index); 566 } 567 } 568 569 /** {@inheritDoc} */ 570 @Override 571 public void setSubVector(int index, RealVector v) 572 throws OutOfRangeException { 573 checkIndex(index); 574 checkIndex(index + v.getDimension() - 1); 575 for (int i = 0; i < v.getDimension(); i++) { 576 setEntry(i + index, v.getEntry(i)); 577 } 578 } 579 580 /** {@inheritDoc} */ 581 @Override 582 public void set(double value) { 583 for (int i = 0; i < virtualSize; i++) { 584 setEntry(i, value); 585 } 586 } 587 588 /** 589 * Optimized method to subtract OpenMapRealVectors. 590 * 591 * @param v Vector to subtract from {@code this}. 592 * @return the difference of {@code this} and {@code v}. 593 * @throws DimensionMismatchException if the dimensions do not match. 594 */ 595 public OpenMapRealVector subtract(OpenMapRealVector v) 596 throws DimensionMismatchException { 597 checkVectorDimensions(v.getDimension()); 598 OpenMapRealVector res = copy(); 599 Iterator iter = v.getEntries().iterator(); 600 while (iter.hasNext()) { 601 iter.advance(); 602 int key = iter.key(); 603 if (entries.containsKey(key)) { 604 res.setEntry(key, entries.get(key) - iter.value()); 605 } else { 606 res.setEntry(key, -iter.value()); 607 } 608 } 609 return res; 610 } 611 612 /** {@inheritDoc} */ 613 @Override 614 public RealVector subtract(RealVector v) 615 throws DimensionMismatchException { 616 checkVectorDimensions(v.getDimension()); 617 if (v instanceof OpenMapRealVector) { 618 return subtract((OpenMapRealVector) v); 619 } else { 620 return super.subtract(v); 621 } 622 } 623 624 /** {@inheritDoc} */ 625 @Override 626 public OpenMapRealVector unitVector() throws MathArithmeticException { 627 OpenMapRealVector res = copy(); 628 res.unitize(); 629 return res; 630 } 631 632 /** {@inheritDoc} */ 633 @Override 634 public void unitize() throws MathArithmeticException { 635 double norm = getNorm(); 636 if (isDefaultValue(norm)) { 637 throw new MathArithmeticException(LocalizedFormats.ZERO_NORM); 638 } 639 Iterator iter = entries.iterator(); 640 while (iter.hasNext()) { 641 iter.advance(); 642 entries.put(iter.key(), iter.value() / norm); 643 } 644 } 645 646 /** {@inheritDoc} */ 647 @Override 648 public double[] toArray() { 649 double[] res = new double[virtualSize]; 650 Iterator iter = entries.iterator(); 651 while (iter.hasNext()) { 652 iter.advance(); 653 res[iter.key()] = iter.value(); 654 } 655 return res; 656 } 657 658 /** 659 * {@inheritDoc} 660 * Implementation Note: This works on exact values, and as a result 661 * it is possible for {@code a.subtract(b)} to be the zero vector, while 662 * {@code a.hashCode() != b.hashCode()}. 663 */ 664 @Override 665 public int hashCode() { 666 final int prime = 31; 667 int result = 1; 668 long temp; 669 temp = Double.doubleToLongBits(epsilon); 670 result = prime * result + (int) (temp ^ (temp >>> 32)); 671 result = prime * result + virtualSize; 672 Iterator iter = entries.iterator(); 673 while (iter.hasNext()) { 674 iter.advance(); 675 temp = Double.doubleToLongBits(iter.value()); 676 result = prime * result + (int) (temp ^ (temp >>32)); 677 } 678 return result; 679 } 680 681 /** 682 * {@inheritDoc} 683 * Implementation Note: This performs an exact comparison, and as a result 684 * it is possible for {@code a.subtract(b}} to be the zero vector, while 685 * {@code a.equals(b) == false}. 686 */ 687 @Override 688 public boolean equals(Object obj) { 689 if (this == obj) { 690 return true; 691 } 692 if (!(obj instanceof OpenMapRealVector)) { 693 return false; 694 } 695 OpenMapRealVector other = (OpenMapRealVector) obj; 696 if (virtualSize != other.virtualSize) { 697 return false; 698 } 699 if (Double.doubleToLongBits(epsilon) != 700 Double.doubleToLongBits(other.epsilon)) { 701 return false; 702 } 703 Iterator iter = entries.iterator(); 704 while (iter.hasNext()) { 705 iter.advance(); 706 double test = other.getEntry(iter.key()); 707 if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) { 708 return false; 709 } 710 } 711 iter = other.getEntries().iterator(); 712 while (iter.hasNext()) { 713 iter.advance(); 714 double test = iter.value(); 715 if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) { 716 return false; 717 } 718 } 719 return true; 720 } 721 722 /** 723 * 724 * @return the percentage of none zero elements as a decimal percent. 725 * @since 2.2 726 */ 727 public double getSparsity() { 728 return (double)entries.size()/(double)getDimension(); 729 } 730 731 /** {@inheritDoc} */ 732 @Override 733 public java.util.Iterator<Entry> sparseIterator() { 734 return new OpenMapSparseIterator(); 735 } 736 737 /** 738 * Implementation of {@code Entry} optimized for OpenMap. 739 * This implementation does not allow arbitrary calls to {@code setIndex} 740 * since the order in which entries are returned is undefined. 741 */ 742 protected class OpenMapEntry extends Entry { 743 /** Iterator pointing to the entry. */ 744 private final Iterator iter; 745 746 /** 747 * Build an entry from an iterator point to an element. 748 * 749 * @param iter Iterator pointing to the entry. 750 */ 751 protected OpenMapEntry(Iterator iter) { 752 this.iter = iter; 753 } 754 755 /** {@inheritDoc} */ 756 @Override 757 public double getValue() { 758 return iter.value(); 759 } 760 761 /** {@inheritDoc} */ 762 @Override 763 public void setValue(double value) { 764 entries.put(iter.key(), value); 765 } 766 767 /** {@inheritDoc} */ 768 @Override 769 public int getIndex() { 770 return iter.key(); 771 } 772 } 773 774 /** 775 * Iterator class to do iteration over just the non-zero elements. 776 * This implementation is fail-fast, so cannot be used to modify 777 * any zero element. 778 */ 779 protected class OpenMapSparseIterator implements java.util.Iterator<Entry> { 780 /** Underlying iterator. */ 781 private final Iterator iter; 782 /** Current entry. */ 783 private final Entry current; 784 785 /** Simple constructor. */ 786 protected OpenMapSparseIterator() { 787 iter = entries.iterator(); 788 current = new OpenMapEntry(iter); 789 } 790 791 /** {@inheritDoc} */ 792 @Override 793 public boolean hasNext() { 794 return iter.hasNext(); 795 } 796 797 /** {@inheritDoc} */ 798 @Override 799 public Entry next() { 800 iter.advance(); 801 return current; 802 } 803 804 /** {@inheritDoc} */ 805 @Override 806 public void remove() { 807 throw new UnsupportedOperationException("Not supported"); 808 } 809 } 810}