View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.linear;
23  
24  import java.io.Serializable;
25  
26  import org.hipparchus.exception.LocalizedCoreFormats;
27  import org.hipparchus.exception.MathIllegalArgumentException;
28  import org.hipparchus.exception.MathRuntimeException;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.OpenIntToDoubleHashMap;
31  import org.hipparchus.util.OpenIntToDoubleHashMap.Iterator;
32  
33  /**
34   * This class implements the {@link RealVector} interface with a
35   * {@link OpenIntToDoubleHashMap} backing store.
36   * <p>
37   *  Caveat: This implementation assumes that, for any {@code x},
38   *  the equality {@code x * 0d == 0d} holds. But it is is not true for
39   *  {@code NaN}. Moreover, zero entries will lose their sign.
40   *  Some operations (that involve {@code NaN} and/or infinities) may
41   *  thus give incorrect results, like multiplications, divisions or
42   *  functions mapping.
43   * </p>
44   */
45  public class OpenMapRealVector extends SparseRealVector
46      implements Serializable {
47      /** Default Tolerance for having a value considered zero. */
48      public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12;
49      /** Serializable version identifier. */
50      private static final long serialVersionUID = 8772222695580707260L;
51      /** Entries of the vector. */
52      private final OpenIntToDoubleHashMap entries;
53      /** Dimension of the vector. */
54      private final int virtualSize;
55      /** Tolerance for having a value considered zero. */
56      private final double epsilon;
57  
58      /**
59       * Build a 0-length vector.
60       * Zero-length vectors may be used to initialized construction of vectors
61       * by data gathering. We start with zero-length and use either the {@link
62       * #OpenMapRealVector(OpenMapRealVector, int)} constructor
63       * or one of the {@code append} method ({@link #append(double)},
64       * {@link #append(RealVector)}) to gather data into this vector.
65       */
66      public OpenMapRealVector() {
67          this(0, DEFAULT_ZERO_TOLERANCE);
68      }
69  
70      /**
71       * Construct a vector of zeroes.
72       *
73       * @param dimension Size of the vector.
74       */
75      public OpenMapRealVector(int dimension) {
76          this(dimension, DEFAULT_ZERO_TOLERANCE);
77      }
78  
79      /**
80       * Construct a vector of zeroes, specifying zero tolerance.
81       *
82       * @param dimension Size of the vector.
83       * @param epsilon Tolerance below which a value considered zero.
84       */
85      public OpenMapRealVector(int dimension, double epsilon) {
86          virtualSize = dimension;
87          entries = new OpenIntToDoubleHashMap(0.0);
88          this.epsilon = epsilon;
89      }
90  
91      /**
92       * Build a resized vector, for use with append.
93       *
94       * @param v Original vector.
95       * @param resize Amount to add.
96       */
97      protected OpenMapRealVector(OpenMapRealVector v, int resize) {
98          virtualSize = v.getDimension() + resize;
99          entries = new OpenIntToDoubleHashMap(v.entries);
100         epsilon = v.epsilon;
101     }
102 
103     /**
104      * Build a vector with known the sparseness (for advanced use only).
105      *
106      * @param dimension Size of the vector.
107      * @param expectedSize The expected number of non-zero entries.
108      */
109     public OpenMapRealVector(int dimension, int expectedSize) {
110         this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE);
111     }
112 
113     /**
114      * Build a vector with known the sparseness and zero tolerance
115      * setting (for advanced use only).
116      *
117      * @param dimension Size of the vector.
118      * @param expectedSize Expected number of non-zero entries.
119      * @param epsilon Tolerance below which a value is considered zero.
120      */
121     public OpenMapRealVector(int dimension, int expectedSize, double epsilon) {
122         virtualSize = dimension;
123         entries = new OpenIntToDoubleHashMap(expectedSize, 0.0);
124         this.epsilon = epsilon;
125     }
126 
127     /**
128      * Create from an array.
129      * Only non-zero entries will be stored.
130      *
131      * @param values Set of values to create from.
132      */
133     public OpenMapRealVector(double[] values) {
134         this(values, DEFAULT_ZERO_TOLERANCE);
135     }
136 
137     /**
138      * Create from an array, specifying zero tolerance.
139      * Only non-zero entries will be stored.
140      *
141      * @param values Set of values to create from.
142      * @param epsilon Tolerance below which a value is considered zero.
143      */
144     public OpenMapRealVector(double[] values, double epsilon) {
145         virtualSize = values.length;
146         entries = new OpenIntToDoubleHashMap(0.0);
147         this.epsilon = epsilon;
148         for (int key = 0; key < values.length; key++) {
149             double value = values[key];
150             if (!isDefaultValue(value)) {
151                 entries.put(key, value);
152             }
153         }
154     }
155 
156     /**
157      * Create from an array.
158      * Only non-zero entries will be stored.
159      *
160      * @param values The set of values to create from
161      */
162     public OpenMapRealVector(Double[] values) {
163         this(values, DEFAULT_ZERO_TOLERANCE);
164     }
165 
166     /**
167      * Create from an array.
168      * Only non-zero entries will be stored.
169      *
170      * @param values Set of values to create from.
171      * @param epsilon Tolerance below which a value is considered zero.
172      */
173     public OpenMapRealVector(Double[] values, double epsilon) {
174         virtualSize = values.length;
175         entries = new OpenIntToDoubleHashMap(0.0);
176         this.epsilon = epsilon;
177         for (int key = 0; key < values.length; key++) {
178             double value = values[key].doubleValue();
179             if (!isDefaultValue(value)) {
180                 entries.put(key, value);
181             }
182         }
183     }
184 
185     /**
186      * Copy constructor.
187      *
188      * @param v Instance to copy from.
189      */
190     public OpenMapRealVector(OpenMapRealVector v) {
191         virtualSize = v.getDimension();
192         entries = new OpenIntToDoubleHashMap(v.getEntries());
193         epsilon = v.epsilon;
194     }
195 
196     /**
197      * Generic copy constructor.
198      *
199      * @param v Instance to copy from.
200      */
201     public OpenMapRealVector(RealVector v) {
202         virtualSize = v.getDimension();
203         entries = new OpenIntToDoubleHashMap(0.0);
204         epsilon = DEFAULT_ZERO_TOLERANCE;
205         for (int key = 0; key < virtualSize; key++) {
206             double value = v.getEntry(key);
207             if (!isDefaultValue(value)) {
208                 entries.put(key, value);
209             }
210         }
211     }
212 
213     /**
214      * Get the entries of this instance.
215      *
216      * @return the entries of this instance.
217      */
218     private OpenIntToDoubleHashMap getEntries() {
219         return entries;
220     }
221 
222     /**
223      * Determine if this value is within epsilon of zero.
224      *
225      * @param value Value to test
226      * @return {@code true} if this value is within epsilon to zero,
227      * {@code false} otherwise.
228      */
229     protected boolean isDefaultValue(double value) {
230         return FastMath.abs(value) < epsilon;
231     }
232 
233     /** {@inheritDoc} */
234     @Override
235     public RealVector add(RealVector v)
236         throws MathIllegalArgumentException {
237         checkVectorDimensions(v.getDimension());
238         if (v instanceof OpenMapRealVector) {
239             return add((OpenMapRealVector) v);
240         } else {
241             return super.add(v);
242         }
243     }
244 
245     /**
246      * Optimized method to add two OpenMapRealVectors.
247      * It copies the larger vector, then iterates over the smaller.
248      *
249      * @param v Vector to add.
250      * @return the sum of {@code this} and {@code v}.
251      * @throws MathIllegalArgumentException if the dimensions do not match.
252      */
253     public OpenMapRealVector add(OpenMapRealVector v)
254         throws MathIllegalArgumentException {
255         checkVectorDimensions(v.getDimension());
256         boolean copyThis = entries.size() > v.entries.size();
257         OpenMapRealVector res = copyThis ? this.copy() : v.copy();
258         Iterator iter = copyThis ? v.entries.iterator() : entries.iterator();
259         OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries;
260         while (iter.hasNext()) {
261             iter.advance();
262             int key = iter.key();
263             if (randomAccess.containsKey(key)) {
264                 res.setEntry(key, randomAccess.get(key) + iter.value());
265             } else {
266                 res.setEntry(key, iter.value());
267             }
268         }
269         return res;
270     }
271 
272     /**
273      * Optimized method to append a OpenMapRealVector.
274      * @param v vector to append
275      * @return The result of appending {@code v} to self
276      */
277     public OpenMapRealVector append(OpenMapRealVector v) {
278         OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
279         Iterator iter = v.entries.iterator();
280         while (iter.hasNext()) {
281             iter.advance();
282             res.setEntry(iter.key() + virtualSize, iter.value());
283         }
284         return res;
285     }
286 
287     /** {@inheritDoc} */
288     @Override
289     public OpenMapRealVector append(RealVector v) {
290         if (v instanceof OpenMapRealVector) {
291             return append((OpenMapRealVector) v);
292         } else {
293             final OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
294             for (int i = 0; i < v.getDimension(); i++) {
295                 res.setEntry(i + virtualSize, v.getEntry(i));
296             }
297             return res;
298         }
299     }
300 
301     /** {@inheritDoc} */
302     @Override
303     public OpenMapRealVector append(double d) {
304         OpenMapRealVector res = new OpenMapRealVector(this, 1);
305         res.setEntry(virtualSize, d);
306         return res;
307     }
308 
309     /**
310      * {@inheritDoc}
311      */
312     @Override
313     public OpenMapRealVector copy() {
314         return new OpenMapRealVector(this);
315     }
316 
317     /** {@inheritDoc} */
318     @Override
319     public OpenMapRealVector ebeDivide(RealVector v)
320         throws MathIllegalArgumentException {
321         checkVectorDimensions(v.getDimension());
322         OpenMapRealVector res = new OpenMapRealVector(this);
323         /*
324          * MATH-803: it is not sufficient to loop through non zero entries of
325          * this only. Indeed, if this[i] = 0d and v[i] = 0d, then
326          * this[i] / v[i] = NaN, and not 0d.
327          */
328         final int n = getDimension();
329         for (int i = 0; i < n; i++) {
330             res.setEntry(i, this.getEntry(i) / v.getEntry(i));
331         }
332         return res;
333     }
334 
335     /** {@inheritDoc} */
336     @Override
337     public OpenMapRealVector ebeMultiply(RealVector v)
338         throws MathIllegalArgumentException {
339         checkVectorDimensions(v.getDimension());
340         OpenMapRealVector res = new OpenMapRealVector(this);
341         Iterator iter = entries.iterator();
342         while (iter.hasNext()) {
343             iter.advance();
344             res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key()));
345         }
346         return res;
347     }
348 
349     /** {@inheritDoc} */
350     @Override
351     public OpenMapRealVector getSubVector(int index, int n)
352         throws MathIllegalArgumentException {
353         checkIndex(index);
354         if (n < 0) {
355             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
356         }
357         checkIndex(index + n - 1);
358         OpenMapRealVector res = new OpenMapRealVector(n);
359         int end = index + n;
360         Iterator iter = entries.iterator();
361         while (iter.hasNext()) {
362             iter.advance();
363             int key = iter.key();
364             if (key >= index && key < end) {
365                 res.setEntry(key - index, iter.value());
366             }
367         }
368         return res;
369     }
370 
371     /** {@inheritDoc} */
372     @Override
373     public int getDimension() {
374         return virtualSize;
375     }
376 
377     /**
378      * Optimized method to compute distance.
379      *
380      * @param v Vector to compute distance to.
381      * @return the distance from {@code this} and {@code v}.
382      * @throws MathIllegalArgumentException if the dimensions do not match.
383      */
384     public double getDistance(OpenMapRealVector v)
385         throws MathIllegalArgumentException {
386         checkVectorDimensions(v.getDimension());
387         Iterator iter = entries.iterator();
388         double res = 0;
389         while (iter.hasNext()) {
390             iter.advance();
391             int key = iter.key();
392             double delta;
393             delta = iter.value() - v.getEntry(key);
394             res += delta * delta;
395         }
396         iter = v.getEntries().iterator();
397         while (iter.hasNext()) {
398             iter.advance();
399             int key = iter.key();
400             if (!entries.containsKey(key)) {
401                 final double value = iter.value();
402                 res += value * value;
403             }
404         }
405         return FastMath.sqrt(res);
406     }
407 
408     /** {@inheritDoc} */
409     @Override
410     public double getDistance(RealVector v) throws MathIllegalArgumentException {
411         checkVectorDimensions(v.getDimension());
412         if (v instanceof OpenMapRealVector) {
413             return getDistance((OpenMapRealVector) v);
414         } else {
415             return super.getDistance(v);
416         }
417     }
418 
419     /** {@inheritDoc} */
420     @Override
421     public double getEntry(int index) throws MathIllegalArgumentException {
422         checkIndex(index);
423         return entries.get(index);
424     }
425 
426     /**
427      * Distance between two vectors.
428      * This method computes the distance consistent with
429      * L<sub>1</sub> norm, i.e. the sum of the absolute values of
430      * elements differences.
431      *
432      * @param v Vector to which distance is requested.
433      * @return distance between this vector and {@code v}.
434      * @throws MathIllegalArgumentException if the dimensions do not match.
435      */
436     public double getL1Distance(OpenMapRealVector v)
437         throws MathIllegalArgumentException {
438         checkVectorDimensions(v.getDimension());
439         double max = 0;
440         Iterator iter = entries.iterator();
441         while (iter.hasNext()) {
442             iter.advance();
443             double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
444             max += delta;
445         }
446         iter = v.getEntries().iterator();
447         while (iter.hasNext()) {
448             iter.advance();
449             int key = iter.key();
450             if (!entries.containsKey(key)) {
451                 double delta = FastMath.abs(iter.value());
452                 max +=  FastMath.abs(delta);
453             }
454         }
455         return max;
456     }
457 
458     /** {@inheritDoc} */
459     @Override
460     public double getL1Distance(RealVector v)
461         throws MathIllegalArgumentException {
462         checkVectorDimensions(v.getDimension());
463         if (v instanceof OpenMapRealVector) {
464             return getL1Distance((OpenMapRealVector) v);
465         } else {
466             return super.getL1Distance(v);
467         }
468     }
469 
470     /**
471      * Optimized method to compute LInfDistance.
472      *
473      * @param v Vector to compute distance from.
474      * @return the LInfDistance.
475      * @throws MathIllegalArgumentException if the dimensions do not match.
476      */
477     private double getLInfDistance(OpenMapRealVector v)
478         throws MathIllegalArgumentException {
479         checkVectorDimensions(v.getDimension());
480         double max = 0;
481         Iterator iter = entries.iterator();
482         while (iter.hasNext()) {
483             iter.advance();
484             double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
485             if (delta > max) {
486                 max = delta;
487             }
488         }
489         iter = v.getEntries().iterator();
490         while (iter.hasNext()) {
491             iter.advance();
492             int key = iter.key();
493             if (!entries.containsKey(key) && iter.value() > max) {
494                 max = iter.value();
495             }
496         }
497         return max;
498     }
499 
500     /** {@inheritDoc} */
501     @Override
502     public double getLInfDistance(RealVector v)
503         throws MathIllegalArgumentException {
504         checkVectorDimensions(v.getDimension());
505         if (v instanceof OpenMapRealVector) {
506             return getLInfDistance((OpenMapRealVector) v);
507         } else {
508             return super.getLInfDistance(v);
509         }
510     }
511 
512     /** {@inheritDoc} */
513     @Override
514     public boolean isInfinite() {
515         boolean infiniteFound = false;
516         Iterator iter = entries.iterator();
517         while (iter.hasNext()) {
518             iter.advance();
519             final double value = iter.value();
520             if (Double.isNaN(value)) {
521                 return false;
522             }
523             if (Double.isInfinite(value)) {
524                 infiniteFound = true;
525             }
526         }
527         return infiniteFound;
528     }
529 
530     /** {@inheritDoc} */
531     @Override
532     public boolean isNaN() {
533         Iterator iter = entries.iterator();
534         while (iter.hasNext()) {
535             iter.advance();
536             if (Double.isNaN(iter.value())) {
537                 return true;
538             }
539         }
540         return false;
541     }
542 
543     /** {@inheritDoc} */
544     @Override
545     public OpenMapRealVector mapAdd(double d) {
546         return copy().mapAddToSelf(d);
547     }
548 
549     /** {@inheritDoc} */
550     @Override
551     public OpenMapRealVector mapAddToSelf(double d) {
552         for (int i = 0; i < virtualSize; i++) {
553             setEntry(i, getEntry(i) + d);
554         }
555         return this;
556     }
557 
558     /** {@inheritDoc} */
559     @Override
560     public void setEntry(int index, double value)
561         throws MathIllegalArgumentException {
562         checkIndex(index);
563         if (!isDefaultValue(value)) {
564             entries.put(index, value);
565         } else if (entries.containsKey(index)) {
566             entries.remove(index);
567         }
568     }
569 
570     /** {@inheritDoc} */
571     @Override
572     public void setSubVector(int index, RealVector v)
573         throws MathIllegalArgumentException {
574         checkIndex(index);
575         checkIndex(index + v.getDimension() - 1);
576         for (int i = 0; i < v.getDimension(); i++) {
577             setEntry(i + index, v.getEntry(i));
578         }
579     }
580 
581     /** {@inheritDoc} */
582     @Override
583     public void set(double value) {
584         for (int i = 0; i < virtualSize; i++) {
585             setEntry(i, value);
586         }
587     }
588 
589     /**
590      * Optimized method to subtract OpenMapRealVectors.
591      *
592      * @param v Vector to subtract from {@code this}.
593      * @return the difference of {@code this} and {@code v}.
594      * @throws MathIllegalArgumentException if the dimensions do not match.
595      */
596     public OpenMapRealVector subtract(OpenMapRealVector v)
597         throws MathIllegalArgumentException {
598         checkVectorDimensions(v.getDimension());
599         OpenMapRealVector res = copy();
600         Iterator iter = v.getEntries().iterator();
601         while (iter.hasNext()) {
602             iter.advance();
603             int key = iter.key();
604             if (entries.containsKey(key)) {
605                 res.setEntry(key, entries.get(key) - iter.value());
606             } else {
607                 res.setEntry(key, -iter.value());
608             }
609         }
610         return res;
611     }
612 
613     /** {@inheritDoc} */
614     @Override
615     public RealVector subtract(RealVector v)
616         throws MathIllegalArgumentException {
617         checkVectorDimensions(v.getDimension());
618         if (v instanceof OpenMapRealVector) {
619             return subtract((OpenMapRealVector) v);
620         } else {
621             return super.subtract(v);
622         }
623     }
624 
625     /** {@inheritDoc} */
626     @Override
627     public OpenMapRealVector unitVector() throws MathRuntimeException {
628         OpenMapRealVector res = copy();
629         res.unitize();
630         return res;
631     }
632 
633     /** {@inheritDoc} */
634     @Override
635     public void unitize() throws MathRuntimeException {
636         double norm = getNorm();
637         if (isDefaultValue(norm)) {
638             throw new MathRuntimeException(LocalizedCoreFormats.ZERO_NORM);
639         }
640         Iterator iter = entries.iterator();
641         while (iter.hasNext()) {
642             iter.advance();
643             entries.put(iter.key(), iter.value() / norm);
644         }
645     }
646 
647     /** {@inheritDoc} */
648     @Override
649     public double[] toArray() {
650         double[] res = new double[virtualSize];
651         Iterator iter = entries.iterator();
652         while (iter.hasNext()) {
653             iter.advance();
654             res[iter.key()] = iter.value();
655         }
656         return res;
657     }
658 
659     /**
660      * {@inheritDoc}
661      * Implementation Note: This works on exact values, and as a result
662      * it is possible for {@code a.subtract(b)} to be the zero vector, while
663      * {@code a.hashCode() != b.hashCode()}.
664      */
665     @Override
666     public int hashCode() {
667         final int prime = 31;
668         int result = 1;
669         long temp;
670         temp = Double.doubleToLongBits(epsilon);
671         result = prime * result + (int) (temp ^ (temp >>> 32));
672         result = prime * result + virtualSize;
673         Iterator iter = entries.iterator();
674         while (iter.hasNext()) {
675             iter.advance();
676             temp = Double.doubleToLongBits(iter.value());
677             result = prime * result + (int) (temp ^ (temp >>32));
678         }
679         return result;
680     }
681 
682     /**
683      * {@inheritDoc}
684      * Implementation Note: This performs an exact comparison, and as a result
685      * it is possible for {@code a.subtract(b}} to be the zero vector, while
686      * {@code  a.equals(b) == false}.
687      */
688     @Override
689     public boolean equals(Object obj) {
690         if (this == obj) {
691             return true;
692         }
693         if (!(obj instanceof OpenMapRealVector)) {
694             return false;
695         }
696         OpenMapRealVector other = (OpenMapRealVector) obj;
697         if (virtualSize != other.virtualSize) {
698             return false;
699         }
700         if (Double.doubleToLongBits(epsilon) !=
701             Double.doubleToLongBits(other.epsilon)) {
702             return false;
703         }
704         Iterator iter = entries.iterator();
705         while (iter.hasNext()) {
706             iter.advance();
707             double test = other.getEntry(iter.key());
708             if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) {
709                 return false;
710             }
711         }
712         iter = other.getEntries().iterator();
713         while (iter.hasNext()) {
714             iter.advance();
715             double test = iter.value();
716             if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) {
717                 return false;
718             }
719         }
720         return true;
721     }
722 
723     /** Get percentage of none zero elements as a decimal percent.
724      * @return the percentage of none zero elements as a decimal percent
725      */
726     public double getSparsity() {
727         return (double)entries.size()/(double)getDimension();
728     }
729 
730     /** {@inheritDoc} */
731     @Override
732     public java.util.Iterator<Entry> sparseIterator() {
733         return new OpenMapSparseIterator();
734     }
735 
736     /**
737      * Implementation of {@code Entry} optimized for OpenMap.
738      * This implementation does not allow arbitrary calls to {@code setIndex}
739      * since the order in which entries are returned is undefined.
740      */
741     protected class OpenMapEntry extends Entry {
742         /** Iterator pointing to the entry. */
743         private final Iterator iter;
744 
745         /**
746          * Build an entry from an iterator point to an element.
747          *
748          * @param iter Iterator pointing to the entry.
749          */
750         protected OpenMapEntry(Iterator iter) {
751             this.iter = iter;
752         }
753 
754         /** {@inheritDoc} */
755         @Override
756         public double getValue() {
757             return iter.value();
758         }
759 
760         /** {@inheritDoc} */
761         @Override
762         public void setValue(double value) {
763             entries.put(iter.key(), value);
764         }
765 
766         /** {@inheritDoc} */
767         @Override
768         public int getIndex() {
769             return iter.key();
770         }
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 }