baku-toolkit/lib/src/main/java/io/v/baku/toolkit/bind/DerivedListDeltaAccumulator.java
/**
         * Transitions from {@code working} to {@code goal} by moving one item at a time. Moves are
         * performed in clusters of like deltas, moving smaller clusters first in the hopes that
         * larger clusters will fall into place as a result of the smaller moves. While this usually
         * results in optimal plans, there are cases where it can result in terrible plan, e.g.
         * <p>
         * 3 4 2 1 -> 3 2 4 1 -> 2 4 3 1 -> 4 2 3 1 -> 2 3 1 4 -> 1 2 3 4
         * <p>
         * I believe we can prevent this pitfall by rejecting moves that would break clusters:
         * <p>
         * 3 4 2 1 -> 1 3 4 2 -> 1 2 3 4
         * <p>
         * TODO(rosswang): Do this (and add a test).
         */
Transitions from  working to  goal by moving one item at a time. Moves are
performed in clusters of like deltas, moving smaller clusters first in the hopes that
larger clusters will fall into place as a result of the smaller moves. While this usually
results in optimal plans, there are cases where it can result in terrible plan, e.g.
Transitions from working to goal by moving one item at a time . Moves are performed in clusters of like deltas moving smaller clusters first in the hopes that larger clusters will fall into place as a result of the smaller moves . While this usually results in optimal plans there are cases where it can result in terrible plan e . g .
void processMoves(final List<T> working, final List<T> goal) {
            final HashMap<T, Integer> goalIndices = new HashMap<>(goal.size());
            for (int i = 0; i < goal.size(); i++) {
                goalIndices.put(goal.get(i), i);
            }
            class MoveProcessor {
                int start, end, clusterStart, clusterDelta,
                        minClusterStart, minClusterEnd, minClusterDelta;
                boolean hasMovingCluster() {
                    return minClusterDelta != 0;
                }
                void startCluster(final int index, final int delta) {
                    clusterStart = index;
                    clusterDelta = delta;
                }
                void endCluster(final int indexAfter) {
                    if (clusterDelta == 0) {
                        if (!hasMovingCluster()) {
                            start = indexAfter;
                        } else if (indexAfter == end) {
                            end = clusterStart;
                        }
                    } else if (!hasMovingCluster() ||
                            indexAfter - clusterStart < minClusterEnd - minClusterStart) {
                        minClusterStart = clusterStart;
                        minClusterEnd = indexAfter;
                        minClusterDelta = clusterDelta;
                    }
                }
                MoveProcessor() {
                    this(0, working.size());
                }
                MoveProcessor(final int start, final int end) {
                    this.start = start;
                    this.end = end;
                    for (int i = start; i < end; i++) {
                        final int delta = goalIndices.get(working.get(i)) - i;
                        if (delta != clusterDelta) {
                            endCluster(i);
                            startCluster(i, delta);
                        }
                    }
                    endCluster(end);
                }
                DerivedListDeltaAccumulator<T> step(final int from, final int to) {
                    working.add(to, working.remove(from));
                    return new DerivedListDeltaAccumulator<T>(ids, produceStepSnapshot(working)) {
                        @Override
                        public void notifyDeltas(RecyclerView.Adapter<?> rva) {
                            rva.notifyItemMoved(from, to);
                        }
                    };
                }
                void addMinClusterSteps() {
                    if (minClusterDelta < 0) {
                        for (int i = minClusterStart; i < minClusterEnd; i++) {
                            steps.add(step(i, i + minClusterDelta));
                        }
                    } else {
                        for (int i = minClusterEnd - 1; i >= minClusterStart; i--) {
                            steps.add(step(i, i + minClusterDelta));
                        }
                    }
                }
            }
            for (MoveProcessor mp = new MoveProcessor(); mp.hasMovingCluster();
                 mp = new MoveProcessor(mp.start, mp.end)) {
                mp.addMinClusterSteps();
            }
        }
void processMoves ( final List < T > working , final List < T > goal ) { final HashMap < T , Integer > goalIndices = new HashMap < > ( goal . size ( ) ) ; for ( int i = 0 ; i < goal . size ( ) ; i ++ ) { goalIndices . put ( goal . get ( i ) , i ) ; } class MoveProcessor { int start , end , clusterStart , clusterDelta , minClusterStart , minClusterEnd , minClusterDelta ; boolean hasMovingCluster ( ) { return minClusterDelta != 0 ; } void startCluster ( final int index , final int delta ) { clusterStart = index ; clusterDelta = delta ; } void endCluster ( final int indexAfter ) { if ( clusterDelta == 0 ) { if ( ! hasMovingCluster ( ) ) { start = indexAfter ; } else if ( indexAfter == end ) { end = clusterStart ; } } else if ( ! hasMovingCluster ( ) || indexAfter - clusterStart < minClusterEnd - minClusterStart ) { minClusterStart = clusterStart ; minClusterEnd = indexAfter ; minClusterDelta = clusterDelta ; } } MoveProcessor ( ) { this ( 0 , working . size ( ) ) ; } MoveProcessor ( final int start , final int end ) { this . start = start ; this . end = end ; for ( int i = start ; i < end ; i ++ ) { final int delta = goalIndices . get ( working . get ( i ) ) - i ; if ( delta != clusterDelta ) { endCluster ( i ) ; startCluster ( i , delta ) ; } } endCluster ( end ) ; } DerivedListDeltaAccumulator < T > step ( final int from , final int to ) { working . add ( to , working . remove ( from ) ) ; return new DerivedListDeltaAccumulator < T > ( ids , produceStepSnapshot ( working ) ) { @ Override public void notifyDeltas ( RecyclerView . Adapter < ? > rva ) { rva . notifyItemMoved ( from , to ) ; } } ; } void addMinClusterSteps ( ) { if ( minClusterDelta < 0 ) { for ( int i = minClusterStart ; i < minClusterEnd ; i ++ ) { steps . add ( step ( i , i + minClusterDelta ) ) ; } } else { for ( int i = minClusterEnd - 1 ; i >= minClusterStart ; i -- ) { steps . add ( step ( i , i + minClusterDelta ) ) ; } } } } for ( MoveProcessor mp = new MoveProcessor ( ) ; mp . hasMovingCluster ( ) ; mp = new MoveProcessor ( mp . start , mp . end ) ) { mp . addMinClusterSteps ( ) ; } }
Transitions from {@code working} to {@code goal} by moving one item at a time.
Transitions from { @code working } to { @code goal } by moving one item at a time .
// smallest cluster; keep it as a prime candidate for moving