diff --git a/community/values/src/main/java/org/neo4j/values/SequenceValue.java b/community/values/src/main/java/org/neo4j/values/SequenceValue.java index 801ecedbd47b4..e4ee400f4fe0d 100644 --- a/community/values/src/main/java/org/neo4j/values/SequenceValue.java +++ b/community/values/src/main/java/org/neo4j/values/SequenceValue.java @@ -20,14 +20,38 @@ package org.neo4j.values; import java.util.Comparator; +import java.util.Iterator; + +import static org.neo4j.values.SequenceValue.IterationPreference.RANDOM_ACCESS; /** * Values that represent sequences of values (such as Lists or Arrays) need to implement this interface. * Thus we can get an equality check that is based on the values (e.g. List.equals(ArrayValue) ) * Values that implement this interface also need to overwrite isSequence() to return true! + * + * Note that even though SequenceValue extends Iterable iterating over the sequence using iterator() might not be the + * most performant method. Branch using iterationPreference() in performance critical code paths. */ -public interface SequenceValue +public interface SequenceValue extends Iterable { + /** + * The preferred way to iterate this sequence. Preferred in this case means the method which is expected to be + * the most performant. + */ + enum IterationPreference + { + RANDOM_ACCESS, + ITERATION + } + + int length(); + + AnyValue value( int offset ); + + Iterator iterator(); + + IterationPreference iterationPreference(); + default boolean equals( SequenceValue other ) { if ( other == null ) @@ -35,45 +59,95 @@ default boolean equals( SequenceValue other ) return false; } - if ( this.length() != other.length() ) + IterationPreference pref = iterationPreference(); + IterationPreference otherPref = other.iterationPreference(); + if ( pref == RANDOM_ACCESS && otherPref == RANDOM_ACCESS ) { - return false; + return equalsUsingRandomAccess( this, other ); } + else + { + return equalsUsingIterators( this, other ); + } + } + + static boolean equalsUsingRandomAccess( SequenceValue a, SequenceValue b ) + { + int i = 0; + boolean areEqual = a.length() == b.length(); - for ( int i = 0; i < this.length(); i++ ) + while ( areEqual && i < a.length() ) { - AnyValue myValue = this.value( i ); - AnyValue otherValue = other.value( i ); - if ( !myValue.equals( otherValue ) ) - { - return false; - } + areEqual = a.value( i ).equals( b.value( i ) ); + i++; } - return true; + return areEqual; } - AnyValue value( int offset ); + static boolean equalsUsingIterators( SequenceValue a, SequenceValue b ) + { + boolean areEqual = true; + Iterator aIterator = a.iterator(); + Iterator bIterator = b.iterator(); - int length(); + while ( areEqual && aIterator.hasNext() && bIterator.hasNext() ) + { + areEqual = aIterator.next().equals( bIterator.next() ); + } + + return areEqual && aIterator.hasNext() == bIterator.hasNext(); + } default int compareToSequence( SequenceValue other, Comparator comparator ) + { + IterationPreference pref = iterationPreference(); + IterationPreference otherPref = other.iterationPreference(); + if ( pref == RANDOM_ACCESS && otherPref == RANDOM_ACCESS ) + { + return compareUsingRandomAccess( this, other, comparator ); + } + else + { + return compareUsingIterators( this, other, comparator ); + } + } + + static int compareUsingRandomAccess( SequenceValue a, SequenceValue b, Comparator comparator ) { int i = 0; int x = 0; - int length = Math.min( length(), other.length() ); + int length = Math.min( a.length(), b.length() ); while ( x == 0 && i < length ) { - x = comparator.compare( value( i ), other.value( i ) ); + x = comparator.compare( a.value( i ), b.value( i ) ); i++; } if ( x == 0 ) { - x = length() - other.length(); + x = a.length() - b.length(); } return x; } + static int compareUsingIterators( SequenceValue a, SequenceValue b, Comparator comparator ) + { + int x = 0; + Iterator aIterator = a.iterator(); + Iterator bIterator = b.iterator(); + + while ( aIterator.hasNext() && bIterator.hasNext() ) + { + x = comparator.compare( aIterator.next(), bIterator.next() ); + } + + if ( x == 0 ) + { + x = Boolean.compare( aIterator.hasNext(), bIterator.hasNext() ); + } + + return x; + } } diff --git a/community/values/src/main/java/org/neo4j/values/storable/ArrayValue.java b/community/values/src/main/java/org/neo4j/values/storable/ArrayValue.java index 9f1fbe9e296f7..df3155641e62c 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/ArrayValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/ArrayValue.java @@ -19,6 +19,9 @@ */ package org.neo4j.values.storable; +import java.util.Iterator; + +import org.neo4j.values.AnyValue; import org.neo4j.values.SequenceValue; /** @@ -28,6 +31,31 @@ public abstract class ArrayValue extends Value implements SequenceValue { public abstract int length(); + public IterationPreference iterationPreference() + { + return IterationPreference.RANDOM_ACCESS; + } + + public Iterator iterator() + { + return new Iterator() + { + private int offset; + + @Override + public boolean hasNext() + { + return offset < length(); + } + + @Override + public AnyValue next() + { + return value( offset ); + } + }; + } + @Override public boolean equals( boolean x ) { diff --git a/community/values/src/main/java/org/neo4j/values/virtual/ListValue.java b/community/values/src/main/java/org/neo4j/values/virtual/ListValue.java index e1686f12576c5..43a7fc2bf41df 100644 --- a/community/values/src/main/java/org/neo4j/values/virtual/ListValue.java +++ b/community/values/src/main/java/org/neo4j/values/virtual/ListValue.java @@ -151,6 +151,12 @@ static final class ArrayValueListValue extends ListValue this.array = array; } + @Override + public IterationPreference iterationPreference() + { + return IterationPreference.RANDOM_ACCESS; + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -219,6 +225,12 @@ static final class ArrayListValue extends ListValue this.values = values; } + @Override + public IterationPreference iterationPreference() + { + return IterationPreference.RANDOM_ACCESS; + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -267,6 +279,12 @@ static final class JavaListListValue extends ListValue this.values = values; } + @Override + public IterationPreference iterationPreference() + { + return IterationPreference.ITERATION; + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -319,6 +337,12 @@ static final class ListSlice extends ListValue this.to = to; } + @Override + public IterationPreference iterationPreference() + { + return inner.iterationPreference(); + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -376,6 +400,12 @@ static final class ReversedList extends ListValue this.inner = inner; } + @Override + public IterationPreference iterationPreference() + { + return inner.iterationPreference(); + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -434,6 +464,12 @@ static final class TransformedListValue extends ListValue this.transform = transform; } + @Override + public IterationPreference iterationPreference() + { + return inner.iterationPreference(); + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -619,6 +655,12 @@ public Iterator iterator() return new FilteredIterator(); } + @Override + public IterationPreference iterationPreference() + { + return IterationPreference.ITERATION; + } + private class FilteredIterator implements Iterator { private AnyValue next; @@ -689,6 +731,12 @@ static final class IntegralRangeListValue extends ListValue this.step = step; } + @Override + public IterationPreference iterationPreference() + { + return IterationPreference.RANDOM_ACCESS; + } + @Override public void writeTo( AnyValueWriter writer ) throws E { @@ -790,6 +838,12 @@ static final class ConcatList extends ListValue this.lists = lists; } + @Override + public IterationPreference iterationPreference() + { + return IterationPreference.ITERATION; + } + @Override public int size() {