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}