Skip to content

Commit

Permalink
Specialize SequenceValue equals and compareTo
Browse files Browse the repository at this point in the history
Some SequenceValue implementations have list like behaviour, and do thus
not support constant speed random access. In these cases it will be much
faster to iterate over the sequence using an Iterator.

We introduce a way to discover the preferred method of iteration from any
sequence value, and specialize the SequenceValue equals and compareTo so
that they are always O(n+m), where n and m are the sizes of the
sequences.
  • Loading branch information
fickludd committed Aug 25, 2017
1 parent 84944ad commit f6fd154
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 16 deletions.
106 changes: 90 additions & 16 deletions community/values/src/main/java/org/neo4j/values/SequenceValue.java
Expand Up @@ -20,60 +20,134 @@
package org.neo4j.values; package org.neo4j.values;


import java.util.Comparator; 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. * 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) ) * 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! * 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<AnyValue>
{ {
/**
* 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<AnyValue> iterator();

IterationPreference iterationPreference();

default boolean equals( SequenceValue other ) default boolean equals( SequenceValue other )
{ {
if ( other == null ) if ( other == null )
{ {
return false; 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 ); areEqual = a.value( i ).equals( b.value( i ) );
AnyValue otherValue = other.value( i ); i++;
if ( !myValue.equals( otherValue ) )
{
return false;
}
} }
return true; return areEqual;
} }


AnyValue value( int offset ); static boolean equalsUsingIterators( SequenceValue a, SequenceValue b )
{
boolean areEqual = true;
Iterator<AnyValue> aIterator = a.iterator();
Iterator<AnyValue> 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<AnyValue> comparator ) default int compareToSequence( SequenceValue other, Comparator<AnyValue> 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<AnyValue> comparator )
{ {
int i = 0; int i = 0;
int x = 0; int x = 0;
int length = Math.min( length(), other.length() ); int length = Math.min( a.length(), b.length() );


while ( x == 0 && i < length ) while ( x == 0 && i < length )
{ {
x = comparator.compare( value( i ), other.value( i ) ); x = comparator.compare( a.value( i ), b.value( i ) );
i++; i++;
} }


if ( x == 0 ) if ( x == 0 )
{ {
x = length() - other.length(); x = a.length() - b.length();
} }


return x; return x;
} }


static int compareUsingIterators( SequenceValue a, SequenceValue b, Comparator<AnyValue> comparator )
{
int x = 0;
Iterator<AnyValue> aIterator = a.iterator();
Iterator<AnyValue> 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;
}
} }
Expand Up @@ -19,6 +19,9 @@
*/ */
package org.neo4j.values.storable; package org.neo4j.values.storable;


import java.util.Iterator;

import org.neo4j.values.AnyValue;
import org.neo4j.values.SequenceValue; import org.neo4j.values.SequenceValue;


/** /**
Expand All @@ -28,6 +31,31 @@ public abstract class ArrayValue extends Value implements SequenceValue
{ {
public abstract int length(); public abstract int length();


public IterationPreference iterationPreference()
{
return IterationPreference.RANDOM_ACCESS;
}

public Iterator<AnyValue> iterator()
{
return new Iterator<AnyValue>()
{
private int offset;

@Override
public boolean hasNext()
{
return offset < length();
}

@Override
public AnyValue next()
{
return value( offset );
}
};
}

@Override @Override
public boolean equals( boolean x ) public boolean equals( boolean x )
{ {
Expand Down
Expand Up @@ -151,6 +151,12 @@ static final class ArrayValueListValue extends ListValue
this.array = array; this.array = array;
} }


@Override
public IterationPreference iterationPreference()
{
return IterationPreference.RANDOM_ACCESS;
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -219,6 +225,12 @@ static final class ArrayListValue extends ListValue
this.values = values; this.values = values;
} }


@Override
public IterationPreference iterationPreference()
{
return IterationPreference.RANDOM_ACCESS;
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -267,6 +279,12 @@ static final class JavaListListValue extends ListValue
this.values = values; this.values = values;
} }


@Override
public IterationPreference iterationPreference()
{
return IterationPreference.ITERATION;
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -319,6 +337,12 @@ static final class ListSlice extends ListValue
this.to = to; this.to = to;
} }


@Override
public IterationPreference iterationPreference()
{
return inner.iterationPreference();
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -376,6 +400,12 @@ static final class ReversedList extends ListValue
this.inner = inner; this.inner = inner;
} }


@Override
public IterationPreference iterationPreference()
{
return inner.iterationPreference();
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -434,6 +464,12 @@ static final class TransformedListValue extends ListValue
this.transform = transform; this.transform = transform;
} }


@Override
public IterationPreference iterationPreference()
{
return inner.iterationPreference();
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -619,6 +655,12 @@ public Iterator<AnyValue> iterator()
return new FilteredIterator(); return new FilteredIterator();
} }


@Override
public IterationPreference iterationPreference()
{
return IterationPreference.ITERATION;
}

private class FilteredIterator implements Iterator<AnyValue> private class FilteredIterator implements Iterator<AnyValue>
{ {
private AnyValue next; private AnyValue next;
Expand Down Expand Up @@ -689,6 +731,12 @@ static final class IntegralRangeListValue extends ListValue
this.step = step; this.step = step;
} }


@Override
public IterationPreference iterationPreference()
{
return IterationPreference.RANDOM_ACCESS;
}

@Override @Override
public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E public <E extends Exception> void writeTo( AnyValueWriter<E> writer ) throws E
{ {
Expand Down Expand Up @@ -790,6 +838,12 @@ static final class ConcatList extends ListValue
this.lists = lists; this.lists = lists;
} }


@Override
public IterationPreference iterationPreference()
{
return IterationPreference.ITERATION;
}

@Override @Override
public int size() public int size()
{ {
Expand Down

0 comments on commit f6fd154

Please sign in to comment.