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    *      http://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  package org.apache.commons.math4.legacy.linear;
18  
19  import java.io.Serializable;
20  
21  import org.apache.commons.math4.legacy.core.Field;
22  import org.apache.commons.math4.legacy.core.FieldElement;
23  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
24  import org.apache.commons.math4.legacy.exception.MathArithmeticException;
25  import org.apache.commons.math4.legacy.exception.NotPositiveException;
26  import org.apache.commons.math4.legacy.exception.NullArgumentException;
27  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
28  import org.apache.commons.math4.legacy.exception.OutOfRangeException;
29  import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
30  import org.apache.commons.math4.legacy.core.MathArrays;
31  
32  /**
33   * This class implements the {@link FieldVector} interface with a {@link OpenIntToFieldHashMap} backing store.
34   * <p>
35   *  Caveat: This implementation assumes that, for any {@code x},
36   *  the equality {@code x * 0d == 0d} holds. But it is is not true for
37   *  {@code NaN}. Moreover, zero entries will lose their sign.
38   *  Some operations (that involve {@code NaN} and/or infinities) may
39   *  thus give incorrect results.
40   * </p>
41   * @param <T> the type of the field elements
42   * @since 2.0
43   */
44  public class SparseFieldVector<T extends FieldElement<T>> implements FieldVector<T>, Serializable {
45      /**  Serialization identifier. */
46      private static final long serialVersionUID = 7841233292190413362L;
47      /** Field to which the elements belong. */
48      private final Field<T> field;
49      /** Entries of the vector. */
50      private final OpenIntToFieldHashMap<T> entries;
51      /** Dimension of the vector. */
52      private final int virtualSize;
53  
54      /**
55       * Build a 0-length vector.
56       * Zero-length vectors may be used to initialize construction of vectors
57       * by data gathering. We start with zero-length and use either the {@link
58       * #SparseFieldVector(SparseFieldVector, int)} constructor
59       * or one of the {@code append} method ({@link #append(FieldVector)} or
60       * {@link #append(SparseFieldVector)}) to gather data into this vector.
61       *
62       * @param field Field to which the elements belong.
63       */
64      public SparseFieldVector(Field<T> field) {
65          this(field, 0);
66      }
67  
68  
69      /**
70       * Construct a vector of zeroes.
71       *
72       * @param field Field to which the elements belong.
73       * @param dimension Size of the vector.
74       */
75      public SparseFieldVector(Field<T> field, int dimension) {
76          this.field = field;
77          virtualSize = dimension;
78          entries = new OpenIntToFieldHashMap<>(field);
79      }
80  
81      /**
82       * Build a resized vector, for use with append.
83       *
84       * @param v Original vector
85       * @param resize Amount to add.
86       */
87      protected SparseFieldVector(SparseFieldVector<T> v, int resize) {
88          field = v.field;
89          virtualSize = v.getDimension() + resize;
90          entries = new OpenIntToFieldHashMap<>(v.entries);
91      }
92  
93  
94      /**
95       * Build a vector with known the sparseness (for advanced use only).
96       *
97       * @param field Field to which the elements belong.
98       * @param dimension Size of the vector.
99       * @param expectedSize Expected number of non-zero entries.
100      */
101     public SparseFieldVector(Field<T> field, int dimension, int expectedSize) {
102         this.field = field;
103         virtualSize = dimension;
104         entries = new OpenIntToFieldHashMap<>(field,expectedSize);
105     }
106 
107     /**
108      * Create from a Field array.
109      * Only non-zero entries will be stored.
110      *
111      * @param field Field to which the elements belong.
112      * @param values Set of values to create from.
113      * @exception NullArgumentException if values is null
114      */
115     public SparseFieldVector(Field<T> field, T[] values) throws NullArgumentException {
116         NullArgumentException.check(values);
117         this.field = field;
118         virtualSize = values.length;
119         entries = new OpenIntToFieldHashMap<>(field);
120         for (int key = 0; key < values.length; key++) {
121             T value = values[key];
122             entries.put(key, value);
123         }
124     }
125 
126     /**
127      * Copy constructor.
128      *
129      * @param v Instance to copy.
130      */
131     public SparseFieldVector(SparseFieldVector<T> v) {
132         field = v.field;
133         virtualSize = v.getDimension();
134         entries = new OpenIntToFieldHashMap<>(v.getEntries());
135     }
136 
137     /**
138      * Get the entries of this instance.
139      *
140      * @return the entries of this instance
141      */
142     private OpenIntToFieldHashMap<T> getEntries() {
143         return entries;
144     }
145 
146     /**
147      * Optimized method to add sparse vectors.
148      *
149      * @param v Vector to add.
150      * @return {@code this + v}.
151      * @throws DimensionMismatchException if {@code v} is not the same size as
152      * {@code this}.
153      */
154     public FieldVector<T> add(SparseFieldVector<T> v)
155         throws DimensionMismatchException {
156         checkVectorDimensions(v.getDimension());
157         SparseFieldVector<T> res = (SparseFieldVector<T>)copy();
158         OpenIntToFieldHashMap<T>.Iterator iter = v.getEntries().iterator();
159         while (iter.hasNext()) {
160             iter.advance();
161             int key = iter.key();
162             T value = iter.value();
163             if (entries.containsKey(key)) {
164                 res.setEntry(key, entries.get(key).add(value));
165             } else {
166                 res.setEntry(key, value);
167             }
168         }
169         return res;
170     }
171 
172     /**
173      * Construct a vector by appending a vector to this vector.
174      *
175      * @param v Vector to append to this one.
176      * @return a new vector.
177      */
178     public FieldVector<T> append(SparseFieldVector<T> v) {
179         SparseFieldVector<T> res = new SparseFieldVector<>(this, v.getDimension());
180         OpenIntToFieldHashMap<T>.Iterator iter = v.entries.iterator();
181         while (iter.hasNext()) {
182             iter.advance();
183             res.setEntry(iter.key() + virtualSize, iter.value());
184         }
185         return res;
186     }
187 
188     /** {@inheritDoc} */
189     @Override
190     public FieldVector<T> append(FieldVector<T> v) {
191         if (v instanceof SparseFieldVector<?>) {
192             return append((SparseFieldVector<T>) v);
193         } else {
194             final int n = v.getDimension();
195             FieldVector<T> res = new SparseFieldVector<>(this, n);
196             for (int i = 0; i < n; i++) {
197                 res.setEntry(i + virtualSize, v.getEntry(i));
198             }
199             return res;
200         }
201     }
202 
203     /** {@inheritDoc}
204      * @exception NullArgumentException if d is null
205      */
206     @Override
207     public FieldVector<T> append(T d) throws NullArgumentException {
208         NullArgumentException.check(d);
209         FieldVector<T> res = new SparseFieldVector<>(this, 1);
210         res.setEntry(virtualSize, d);
211         return res;
212      }
213 
214     /** {@inheritDoc} */
215     @Override
216     public FieldVector<T> copy() {
217         return new SparseFieldVector<>(this);
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public T dotProduct(FieldVector<T> v) throws DimensionMismatchException {
223         checkVectorDimensions(v.getDimension());
224         T res = field.getZero();
225         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
226         while (iter.hasNext()) {
227             iter.advance();
228             res = res.add(v.getEntry(iter.key()).multiply(iter.value()));
229         }
230         return res;
231     }
232 
233     /** {@inheritDoc} */
234     @Override
235     public FieldVector<T> ebeDivide(FieldVector<T> v)
236         throws DimensionMismatchException, MathArithmeticException {
237         checkVectorDimensions(v.getDimension());
238         SparseFieldVector<T> res = new SparseFieldVector<>(this);
239         OpenIntToFieldHashMap<T>.Iterator iter = res.entries.iterator();
240         while (iter.hasNext()) {
241             iter.advance();
242             res.setEntry(iter.key(), iter.value().divide(v.getEntry(iter.key())));
243         }
244         return res;
245     }
246 
247     /** {@inheritDoc} */
248     @Override
249     public FieldVector<T> ebeMultiply(FieldVector<T> v)
250         throws DimensionMismatchException {
251         checkVectorDimensions(v.getDimension());
252         SparseFieldVector<T> res = new SparseFieldVector<>(this);
253         OpenIntToFieldHashMap<T>.Iterator iter = res.entries.iterator();
254         while (iter.hasNext()) {
255             iter.advance();
256             res.setEntry(iter.key(), iter.value().multiply(v.getEntry(iter.key())));
257         }
258         return res;
259     }
260 
261     /** {@inheritDoc} */
262     @Override
263     public int getDimension() {
264         return virtualSize;
265     }
266 
267     /** {@inheritDoc} */
268     @Override
269     public T getEntry(int index) throws OutOfRangeException {
270         checkIndex(index);
271         return entries.get(index);
272    }
273 
274     /** {@inheritDoc} */
275     @Override
276     public Field<T> getField() {
277         return field;
278     }
279 
280     /** {@inheritDoc} */
281     @Override
282     public FieldVector<T> getSubVector(int index, int n)
283         throws OutOfRangeException, NotPositiveException {
284         if (n < 0) {
285             throw new NotPositiveException(LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
286         }
287         checkIndex(index);
288         checkIndex(index + n - 1);
289         SparseFieldVector<T> res = new SparseFieldVector<>(field,n);
290         int end = index + n;
291         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
292         while (iter.hasNext()) {
293             iter.advance();
294             int key = iter.key();
295             if (key >= index && key < end) {
296                 res.setEntry(key - index, iter.value());
297             }
298         }
299         return res;
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public FieldVector<T> mapAdd(T d) throws NullArgumentException {
305         return copy().mapAddToSelf(d);
306     }
307 
308     /** {@inheritDoc} */
309     @Override
310     public FieldVector<T> mapAddToSelf(T d) throws NullArgumentException {
311         for (int i = 0; i < virtualSize; i++) {
312             setEntry(i, getEntry(i).add(d));
313         }
314         return this;
315     }
316 
317     /** {@inheritDoc} */
318     @Override
319     public FieldVector<T> mapDivide(T d)
320         throws NullArgumentException, MathArithmeticException {
321         return copy().mapDivideToSelf(d);
322     }
323 
324     /** {@inheritDoc} */
325     @Override
326     public FieldVector<T> mapDivideToSelf(T d)
327         throws NullArgumentException, MathArithmeticException {
328         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
329         while (iter.hasNext()) {
330             iter.advance();
331             entries.put(iter.key(), iter.value().divide(d));
332         }
333         return this;
334     }
335 
336     /** {@inheritDoc} */
337     @Override
338     public FieldVector<T> mapInv() throws MathArithmeticException {
339         return copy().mapInvToSelf();
340     }
341 
342     /** {@inheritDoc} */
343     @Override
344     public FieldVector<T> mapInvToSelf() throws MathArithmeticException {
345         for (int i = 0; i < virtualSize; i++) {
346             setEntry(i, field.getOne().divide(getEntry(i)));
347         }
348         return this;
349     }
350 
351     /** {@inheritDoc} */
352     @Override
353     public FieldVector<T> mapMultiply(T d) throws NullArgumentException {
354         return copy().mapMultiplyToSelf(d);
355     }
356 
357     /** {@inheritDoc} */
358     @Override
359     public FieldVector<T> mapMultiplyToSelf(T d) throws NullArgumentException {
360         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
361         while (iter.hasNext()) {
362             iter.advance();
363             entries.put(iter.key(), iter.value().multiply(d));
364         }
365         return this;
366     }
367 
368     /** {@inheritDoc} */
369     @Override
370     public FieldVector<T> mapSubtract(T d) throws NullArgumentException {
371         return copy().mapSubtractToSelf(d);
372     }
373 
374     /** {@inheritDoc} */
375     @Override
376     public FieldVector<T> mapSubtractToSelf(T d) throws NullArgumentException {
377         return mapAddToSelf(field.getZero().subtract(d));
378     }
379 
380     /**
381      * Optimized method to compute outer product when both vectors are sparse.
382      * @param v vector with which outer product should be computed
383      * @return the matrix outer product between instance and v
384      */
385     public FieldMatrix<T> outerProduct(SparseFieldVector<T> v) {
386         final int n = v.getDimension();
387         SparseFieldMatrix<T> res = new SparseFieldMatrix<>(field, virtualSize, n);
388         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
389         while (iter.hasNext()) {
390             iter.advance();
391             OpenIntToFieldHashMap<T>.Iterator iter2 = v.entries.iterator();
392             while (iter2.hasNext()) {
393                 iter2.advance();
394                 res.setEntry(iter.key(), iter2.key(), iter.value().multiply(iter2.value()));
395             }
396         }
397         return res;
398     }
399 
400     /** {@inheritDoc} */
401     @Override
402     public FieldMatrix<T> outerProduct(FieldVector<T> v) {
403         if (v instanceof SparseFieldVector<?>) {
404             return outerProduct((SparseFieldVector<T>)v);
405         } else {
406             final int n = v.getDimension();
407             FieldMatrix<T> res = new SparseFieldMatrix<>(field, virtualSize, n);
408             OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
409             while (iter.hasNext()) {
410                 iter.advance();
411                 int row = iter.key();
412                 FieldElement<T> value = iter.value();
413                 for (int col = 0; col < n; col++) {
414                     res.setEntry(row, col, value.multiply(v.getEntry(col)));
415                 }
416             }
417             return res;
418         }
419     }
420 
421     /** {@inheritDoc} */
422     @Override
423     public FieldVector<T> projection(FieldVector<T> v)
424         throws DimensionMismatchException, MathArithmeticException {
425         checkVectorDimensions(v.getDimension());
426         return v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
427     }
428 
429     /** {@inheritDoc}
430      * @exception NullArgumentException if value is null
431      */
432     @Override
433     public void set(T value) {
434         NullArgumentException.check(value);
435         for (int i = 0; i < virtualSize; i++) {
436             setEntry(i, value);
437         }
438     }
439 
440     /** {@inheritDoc}
441      * @exception NullArgumentException if value is null
442      */
443     @Override
444     public void setEntry(int index, T value) throws NullArgumentException, OutOfRangeException {
445         NullArgumentException.check(value);
446         checkIndex(index);
447         entries.put(index, value);
448     }
449 
450     /** {@inheritDoc} */
451     @Override
452     public void setSubVector(int index, FieldVector<T> v)
453         throws OutOfRangeException {
454         checkIndex(index);
455         checkIndex(index + v.getDimension() - 1);
456         final int n = v.getDimension();
457         for (int i = 0; i < n; i++) {
458             setEntry(i + index, v.getEntry(i));
459         }
460     }
461 
462     /**
463      * Optimized method to compute {@code this} minus {@code v}.
464      * @param v vector to be subtracted
465      * @return {@code this - v}
466      * @throws DimensionMismatchException if {@code v} is not the same size as
467      * {@code this}.
468      */
469     public SparseFieldVector<T> subtract(SparseFieldVector<T> v)
470         throws DimensionMismatchException {
471         checkVectorDimensions(v.getDimension());
472         SparseFieldVector<T> res = (SparseFieldVector<T>)copy();
473         OpenIntToFieldHashMap<T>.Iterator iter = v.getEntries().iterator();
474         while (iter.hasNext()) {
475             iter.advance();
476             int key = iter.key();
477             if (entries.containsKey(key)) {
478                 res.setEntry(key, entries.get(key).subtract(iter.value()));
479             } else {
480                 res.setEntry(key, field.getZero().subtract(iter.value()));
481             }
482         }
483         return res;
484     }
485 
486     /** {@inheritDoc} */
487     @Override
488     public FieldVector<T> subtract(FieldVector<T> v)
489         throws DimensionMismatchException {
490         if (v instanceof SparseFieldVector<?>) {
491             return subtract((SparseFieldVector<T>)v);
492         } else {
493             final int n = v.getDimension();
494             checkVectorDimensions(n);
495             SparseFieldVector<T> res = new SparseFieldVector<>(this);
496             for (int i = 0; i < n; i++) {
497                 if (entries.containsKey(i)) {
498                     res.setEntry(i, entries.get(i).subtract(v.getEntry(i)));
499                 } else {
500                     res.setEntry(i, field.getZero().subtract(v.getEntry(i)));
501                 }
502             }
503             return res;
504         }
505     }
506 
507     /** {@inheritDoc} */
508     @Override
509     public T[] toArray() {
510         T[] res = MathArrays.buildArray(field, virtualSize);
511         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
512         while (iter.hasNext()) {
513             iter.advance();
514             res[iter.key()] = iter.value();
515         }
516         return res;
517     }
518 
519     /**
520      * Check whether an index is valid.
521      *
522      * @param index Index to check.
523      * @throws OutOfRangeException if the index is not valid.
524      */
525     private void checkIndex(final int index) throws OutOfRangeException {
526         if (index < 0 || index >= getDimension()) {
527             throw new OutOfRangeException(index, 0, getDimension() - 1);
528         }
529     }
530 
531     /**
532      * Checks that the indices of a subvector are valid.
533      *
534      * @param start the index of the first entry of the subvector
535      * @param end the index of the last entry of the subvector (inclusive)
536      * @throws OutOfRangeException if {@code start} of {@code end} are not valid
537      * @throws NumberIsTooSmallException if {@code end < start}
538      * @since 3.3
539      */
540     private void checkIndices(final int start, final int end)
541         throws NumberIsTooSmallException, OutOfRangeException {
542         final int dim = getDimension();
543         if (start < 0 || start >= dim) {
544             throw new OutOfRangeException(LocalizedFormats.INDEX, start, 0,
545                                           dim - 1);
546         }
547         if (end < 0 || end >= dim) {
548             throw new OutOfRangeException(LocalizedFormats.INDEX, end, 0,
549                                           dim - 1);
550         }
551         if (end < start) {
552             throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
553                                                 end, start, false);
554         }
555     }
556 
557     /**
558      * Check if instance dimension is equal to some expected value.
559      *
560      * @param n Expected dimension.
561      * @throws DimensionMismatchException if the dimensions do not match.
562      */
563     protected void checkVectorDimensions(int n)
564         throws DimensionMismatchException {
565         if (getDimension() != n) {
566             throw new DimensionMismatchException(getDimension(), n);
567         }
568     }
569 
570     /** {@inheritDoc} */
571     @Override
572     public FieldVector<T> add(FieldVector<T> v) throws DimensionMismatchException {
573         if (v instanceof SparseFieldVector<?>) {
574             return add((SparseFieldVector<T>) v);
575         } else {
576             final int n = v.getDimension();
577             checkVectorDimensions(n);
578             SparseFieldVector<T> res = new SparseFieldVector<>(field,
579                                                                 getDimension());
580             for (int i = 0; i < n; i++) {
581                 res.setEntry(i, v.getEntry(i).add(getEntry(i)));
582             }
583             return res;
584         }
585     }
586 
587     /**
588      * Visits (but does not alter) all entries of this vector in default order
589      * (increasing index).
590      *
591      * @param visitor the visitor to be used to process the entries of this
592      * vector
593      * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
594      * at the end of the walk
595      * @since 3.3
596      */
597     public T walkInDefaultOrder(final FieldVectorPreservingVisitor<T> visitor) {
598         final int dim = getDimension();
599         visitor.start(dim, 0, dim - 1);
600         for (int i = 0; i < dim; i++) {
601             visitor.visit(i, getEntry(i));
602         }
603         return visitor.end();
604     }
605 
606     /**
607      * Visits (but does not alter) some entries of this vector in default order
608      * (increasing index).
609      *
610      * @param visitor visitor to be used to process the entries of this vector
611      * @param start the index of the first entry to be visited
612      * @param end the index of the last entry to be visited (inclusive)
613      * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
614      * at the end of the walk
615      * @throws NumberIsTooSmallException if {@code end < start}.
616      * @throws OutOfRangeException if the indices are not valid.
617      * @since 3.3
618      */
619     public T walkInDefaultOrder(final FieldVectorPreservingVisitor<T> visitor,
620                                 final int start, final int end)
621         throws NumberIsTooSmallException, OutOfRangeException {
622         checkIndices(start, end);
623         visitor.start(getDimension(), start, end);
624         for (int i = start; i <= end; i++) {
625             visitor.visit(i, getEntry(i));
626         }
627         return visitor.end();
628     }
629 
630     /**
631      * Visits (but does not alter) all entries of this vector in optimized
632      * order. The order in which the entries are visited is selected so as to
633      * lead to the most efficient implementation; it might depend on the
634      * concrete implementation of this abstract class.
635      *
636      * @param visitor the visitor to be used to process the entries of this
637      * vector
638      * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
639      * at the end of the walk
640      * @since 3.3
641      */
642     public T walkInOptimizedOrder(final FieldVectorPreservingVisitor<T> visitor) {
643         return walkInDefaultOrder(visitor);
644     }
645 
646     /**
647      * Visits (but does not alter) some entries of this vector in optimized
648      * order. The order in which the entries are visited is selected so as to
649      * lead to the most efficient implementation; it might depend on the
650      * concrete implementation of this abstract class.
651      *
652      * @param visitor visitor to be used to process the entries of this vector
653      * @param start the index of the first entry to be visited
654      * @param end the index of the last entry to be visited (inclusive)
655      * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
656      * at the end of the walk
657      * @throws NumberIsTooSmallException if {@code end < start}.
658      * @throws OutOfRangeException if the indices are not valid.
659      * @since 3.3
660      */
661     public T walkInOptimizedOrder(final FieldVectorPreservingVisitor<T> visitor,
662                                   final int start, final int end)
663         throws NumberIsTooSmallException, OutOfRangeException {
664         return walkInDefaultOrder(visitor, start, end);
665     }
666 
667     /**
668      * Visits (and possibly alters) all entries of this vector in default order
669      * (increasing index).
670      *
671      * @param visitor the visitor to be used to process and modify the entries
672      * of this vector
673      * @return the value returned by {@link FieldVectorChangingVisitor#end()}
674      * at the end of the walk
675      * @since 3.3
676      */
677     public T walkInDefaultOrder(final FieldVectorChangingVisitor<T> visitor) {
678         final int dim = getDimension();
679         visitor.start(dim, 0, dim - 1);
680         for (int i = 0; i < dim; i++) {
681             setEntry(i, visitor.visit(i, getEntry(i)));
682         }
683         return visitor.end();
684     }
685 
686     /**
687      * Visits (and possibly alters) some entries of this vector in default order
688      * (increasing index).
689      *
690      * @param visitor visitor to be used to process the entries of this vector
691      * @param start the index of the first entry to be visited
692      * @param end the index of the last entry to be visited (inclusive)
693      * @return the value returned by {@link FieldVectorChangingVisitor#end()}
694      * at the end of the walk
695      * @throws NumberIsTooSmallException if {@code end < start}.
696      * @throws OutOfRangeException if the indices are not valid.
697      * @since 3.3
698      */
699     public T walkInDefaultOrder(final FieldVectorChangingVisitor<T> visitor,
700                                 final int start, final int end)
701         throws NumberIsTooSmallException, OutOfRangeException {
702         checkIndices(start, end);
703         visitor.start(getDimension(), start, end);
704         for (int i = start; i <= end; i++) {
705             setEntry(i, visitor.visit(i, getEntry(i)));
706         }
707         return visitor.end();
708     }
709 
710     /**
711      * Visits (and possibly alters) all entries of this vector in optimized
712      * order. The order in which the entries are visited is selected so as to
713      * lead to the most efficient implementation; it might depend on the
714      * concrete implementation of this abstract class.
715      *
716      * @param visitor the visitor to be used to process the entries of this
717      * vector
718      * @return the value returned by {@link FieldVectorChangingVisitor#end()}
719      * at the end of the walk
720      * @since 3.3
721      */
722     public T walkInOptimizedOrder(final FieldVectorChangingVisitor<T> visitor) {
723         return walkInDefaultOrder(visitor);
724     }
725 
726     /**
727      * Visits (and possibly change) some entries of this vector in optimized
728      * order. The order in which the entries are visited is selected so as to
729      * lead to the most efficient implementation; it might depend on the
730      * concrete implementation of this abstract class.
731      *
732      * @param visitor visitor to be used to process the entries of this vector
733      * @param start the index of the first entry to be visited
734      * @param end the index of the last entry to be visited (inclusive)
735      * @return the value returned by {@link FieldVectorChangingVisitor#end()}
736      * at the end of the walk
737      * @throws NumberIsTooSmallException if {@code end < start}.
738      * @throws OutOfRangeException if the indices are not valid.
739      * @since 3.3
740      */
741     public T walkInOptimizedOrder(final FieldVectorChangingVisitor<T> visitor,
742                                   final int start, final int end)
743         throws NumberIsTooSmallException, OutOfRangeException {
744         return walkInDefaultOrder(visitor, start, end);
745     }
746 
747     /** {@inheritDoc} */
748     @Override
749     public int hashCode() {
750         final int prime = 31;
751         int result = 1;
752         result = prime * result + ((field == null) ? 0 : field.hashCode());
753         result = prime * result + virtualSize;
754         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
755         while (iter.hasNext()) {
756             iter.advance();
757             int temp = iter.value().hashCode();
758             result = prime * result + temp;
759         }
760         return result;
761     }
762 
763 
764     /** {@inheritDoc} */
765     @Override
766     public boolean equals(Object obj) {
767 
768         if (this == obj) {
769             return true;
770         }
771 
772         if (!(obj instanceof SparseFieldVector<?>)) {
773             return false;
774         }
775 
776         @SuppressWarnings("unchecked") // OK, because "else if" check below ensures that
777                                        // other must be the same type as this
778         SparseFieldVector<T> other = (SparseFieldVector<T>) obj;
779         if (field == null) {
780             if (other.field != null) {
781                 return false;
782             }
783         } else if (!field.equals(other.field)) {
784             return false;
785         }
786         if (virtualSize != other.virtualSize) {
787             return false;
788         }
789 
790         OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
791         while (iter.hasNext()) {
792             iter.advance();
793             T test = other.getEntry(iter.key());
794             if (!test.equals(iter.value())) {
795                 return false;
796             }
797         }
798         iter = other.getEntries().iterator();
799         while (iter.hasNext()) {
800             iter.advance();
801             T test = iter.value();
802             if (!test.equals(getEntry(iter.key()))) {
803                 return false;
804             }
805         }
806         return true;
807     }
808 }