All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.transit.raptor.util.paretoset.ParetoSet Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
package org.opentripplanner.transit.raptor.util.paretoset;

import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * This {@link java.util.Collection} store all pareto-optimal elements. The
 * {@link #add(Object)} method returns {@code true} if and only if the element
 * was added successfully. When an element is added other elements which are no
 * longer pareto-optimal are dropped.
 * 

* Like the {@link java.util.ArrayList} the elements are stored internally in * an array for performance reasons, and the order is guaranteed to be the same * as the order the elements are added. New elements are added at the end, while * dominated elements are removed. Elements in between are shifted towards the * beginning of the list: *

* {@code [[1,7], [3,5], [5,3]] + [2,4] => [[1,7], [5,3], [2,4]] -- less than dominates} *

* No methods for removing elements like {@link #remove(Object)} are supported. * * @param the element type */ public class ParetoSet extends AbstractCollection { private final ParetoComparator comparator; private final ParetoSetEventListener eventListener; @SuppressWarnings("unchecked") private T[] elements = (T[])new Object[16]; private int size = 0; /** * Create a new ParetoSet with a comparator and a drop event listener. * * @param comparator The comparator to use with this set * @param eventListener At most one listener can be registered to listen for drop events. */ public ParetoSet(ParetoComparator comparator, ParetoSetEventListener eventListener) { this.comparator = comparator; this.eventListener = eventListener; } /** * Create a new ParetoSet with a comparator. */ public ParetoSet(ParetoComparator comparator) { this(comparator, null); } public T get(int index) { return elements[index]; } @Override public int size() { return size; } /** * Return an iterator over the contained collection. *

* This is NOT thread-safe and the behavior is undefined if the collection is modified during * the iteration. */ @Override public final Iterator iterator() { return tailIterator(0); } /** * Return an iterable instance. This is made to be as FAST AS POSSIBLE, sacrificing * thread-safety and modifiable protection. *

* The iterator created by this iterable is NOT thread-safe. *

* Do not modify the collection between the iterator is created, until the iterator complete. *

* It is safe to create as many iterators as you like. The iterator created will reflect the * current set of elements in this set at the time of creation. * * @param startIndexInclusive the first element to include in the iterator, unlike the elements * in this set the index is cashed until an iterator is created. */ final Iterable tail(final int startIndexInclusive) { return () -> tailIterator(startIndexInclusive); } @Override public boolean add(T newValue) { if (size == 0) { acceptAndAppendValue(newValue); return true; } boolean mutualDominanceExist = false; boolean equivalentVectorExist = false; for (int i = 0; i < size; ++i) { T it = elements[i]; boolean leftDominance = leftDominanceExist(newValue, it); boolean rightDominance = rightDominanceExist(newValue, it); if (leftDominance && rightDominance) { mutualDominanceExist = true; } else if (leftDominance) { removeDominatedElementsFromRestOfSetAndAddNewElement(newValue, i); return true; } else if (rightDominance) { notifyElementRejected(newValue, it); return false; } else { equivalentVectorExist = true; } } if (mutualDominanceExist && !equivalentVectorExist) { assertEnoughSpaceInSet(); acceptAndAppendValue(newValue); return true; } // No dominance found, newValue is equivalent with all values in the set notifyElementRejected(newValue, elements[0]); return false; } /** * Test if an element qualify - the element is NOT added. Use the {@link #add(T)} * method directly if the purpose is to add the new element to the collection. *

* Both methods are optimized for performance; hence the add method does not use this method. */ public boolean qualify(T newValue) { if (size == 0) { return true; } boolean mutualDominanceExist = false; boolean equivalentVectorExist = false; for (int i = 0; i < size; ++i) { boolean leftDominance = leftDominanceExist(newValue, elements[i]); boolean rightDominance = rightDominanceExist(newValue, elements[i]); if (leftDominance && rightDominance) { if(equivalentVectorExist) { return false; } mutualDominanceExist = true; } else if (leftDominance) { return true; } else if (rightDominance) { return false; } else { if(mutualDominanceExist) { return false; } equivalentVectorExist = true; } } return mutualDominanceExist; } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public void clear() { size = 0; } /** * This is used for logging and tuning purposes - by looking at the statistics we can decide * a good value for the initial size. */ public final int internalArrayLength() { return elements.length; } /** * A special toSting method which allows the caller to provide a to-string-mapper for the * elements in the set. */ public String toString(Function toStringMapper) { return "{" + Arrays.stream(elements, 0, size) .map(toStringMapper) .collect(Collectors.joining(", ")) + "}"; } @Override public String toString() { return toString(Objects::toString); } /** * Notify subclasses about reindexing. This method is empty, * and only exist for subclasses to override it. */ protected void notifyElementMoved(int fromIndex, int toIndex) { // Noop } /** * This tail iterator is made to be FAST, it is NOT thread-safe and it the * underlying collection is changed the returned values of the iterator also * changes. Do not update on this collection while using this iterator. */ private Iterator tailIterator(final int startInclusive) { return new Iterator<>() { int i = startInclusive; @Override public boolean hasNext() { return i < size; } @Override public T next() { return elements[i++]; } }; } /** * Remove all elements dominated by the {@code newValue} starting from * {@code index + 1}. The element at {@code index} is dropped. */ private void removeDominatedElementsFromRestOfSetAndAddNewElement(final T newValue, final int index) { // Let 'i' be the current element index for removal int i = index; // Let 'j' be the next element to compare int j = index + 1; notifyElementDropped(elements[i], newValue); while (j < size) { notifyElementMoved(j, i); // Move next element(j) forward if it is not dominated by the new value if (!leftVectorDominatesRightVector(newValue, elements[j])) { elements[i] = elements[j]; ++i; } else { notifyElementDropped(elements[j], newValue); } // Goto the next element ++j; } notifyElementMoved(j, i); notifyElementAccepted(newValue); elements[i] = newValue; size = i+1; } private boolean leftVectorDominatesRightVector(T left, T right) { return leftDominanceExist(left, right) && !rightDominanceExist(left, right); } private void acceptAndAppendValue(T newValue) { notifyElementAccepted(newValue); elements[size++] = newValue; } private void assertEnoughSpaceInSet() { if (size == elements.length) { elements = Arrays.copyOf(elements, elements.length * 2); } } private boolean leftDominanceExist(T left, T right) { return comparator.leftDominanceExist(left, right); } private boolean rightDominanceExist(T left, T right) { return comparator.leftDominanceExist(right, left); } private void notifyElementAccepted(T newElement) { if(eventListener != null) { eventListener.notifyElementAccepted(newElement); } } private void notifyElementDropped(T element, T droppedByElement) { if(eventListener != null) { eventListener.notifyElementDropped(element, droppedByElement); } } private void notifyElementRejected(T element, T rejectByElement) { if(eventListener != null) { eventListener.notifyElementRejected(element, rejectByElement); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy