package org.neo4j.graphalgo.impl.util; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.PriorityQueue; class PriorityMap<E, K, P> { public interface Converter<T, S> { T convert( S source ); } public static final class Entry<E, P> { private final E entity; private final P priority; private Entry( E entity, P priority ) { this.entity = entity; this.priority = priority; } Entry( Node<E, P> node ) { this( node.head.entity, node.priority ); } public E getEntity() { return entity; } public P getPriority() { return priority; } } @SuppressWarnings( "unchecked" ) private static final Converter SELF_KEY = new Converter() { public Object convert( Object source ) { return source; } }; @SuppressWarnings( "unchecked" ) public static <K, P> PriorityMap<K, K, P> withSelfKey( Comparator<P> priority ) { return new PriorityMap<K, K, P>( SELF_KEY, priority ); } private static class NaturalPriority<P extends Comparable<P>> implements Comparator<P> { private final boolean reversed; NaturalPriority( boolean reversed ) { this.reversed = reversed; } public int compare( P o1, P o2 ) { if ( reversed ) { return o2.compareTo( o1 ); } else { return o1.compareTo( o2 ); } } } public static <E, K, P extends Comparable<P>> PriorityMap<E, K, P> withNaturalOrder( Converter<K, E> key ) { return PriorityMap.<E, K, P>withNaturalOrder( key, false ); } public static <E, K, P extends Comparable<P>> PriorityMap<E, K, P> withNaturalOrder( Converter<K, E> key, boolean reversed ) { Comparator<P> priority = new NaturalPriority<P>( reversed ); return new PriorityMap<E, K, P>( key, priority ); } public static <K, P extends Comparable<P>> PriorityMap<K, K, P> withSelfKeyNaturalOrder() { return PriorityMap.<K, P>withSelfKeyNaturalOrder( false ); } @SuppressWarnings( "unchecked" ) public static <K, P extends Comparable<P>> PriorityMap<K, K, P> withSelfKeyNaturalOrder( boolean reversed ) { Comparator<P> priority = new NaturalPriority<P>( reversed ); return new PriorityMap<K, K, P>( SELF_KEY, priority ); } private final Converter<K, E> keyFunction; private final Comparator<P> order; private PriorityMap( Converter<K, E> key, Comparator<P> priority ) { this.keyFunction = key; this.order = priority; } /** * Add an entity to the priority map. If the key for the {@code entity} * was already found in the priority map and the priority is the same * the entity will be added. If the priority is lower the existing entities * for that key will be discarded. * * @param entity the entity to add. * @param priority the priority of the entity. * @return whether or not the entity (with its priority) was added to the * priority map. Will return {@code false} iff the key for the entity * already exist and its priority is better than the given * {@code priority}. */ public boolean put( E entity, P priority ) { K key = keyFunction.convert( entity ); Node<E, P> node = map.get( key ); boolean result = false; if ( node != null ) { if ( priority.equals( node.priority ) ) { node.head = new Link<E>( entity, node.head ); result = true; } else if ( order.compare( priority, node.priority ) < 0 ) { queue.remove( node ); put( entity, priority, key ); result = true; } } else { put( entity, priority, key ); result = true; } return result; } private void put( E entity, P priority, K key ) { Node<E, P> node = new Node<E, P>( entity, priority ); map.put( key, node ); queue.add( node ); } /** * Get the priority for the entity with the specified key. * * @param key the key. * @return the priority for the the entity with the specified key. */ public P get( K key ) { Node<E, P> node = map.get( key ); if ( node == null ) return null; return node.priority; } /** * Remove and return the entry with the highest priority. * * @return the entry with the highest priority. */ public Entry<E, P> pop() { Node<E, P> node = queue.peek(); Entry<E, P> result = null; if ( node == null ) { return null; } else if ( node.head.next == null ) { node = queue.poll(); map.remove( keyFunction.convert( node.head.entity ) ); result = new Entry<E, P>( node ); } else { result = new Entry<E, P>( node ); node.head = node.head.next; } return result; } public Entry<E, P> peek() { Node<E, P> node = queue.peek(); if ( node == null ) { return null; } return new Entry<E, P>( node ); } // Naive implementation private final Map<K, Node<E, P>> map = new HashMap<K, Node<E, P>>(); private final PriorityQueue<Node<E, P>> queue = new PriorityQueue<Node<E, P>>( 11, new Comparator<Node<E, P>>() { public int compare( Node<E, P> o1, Node<E, P> o2 ) { return order.compare( o1.priority, o2.priority ); } } ); private static class Node<E, P> { Link<E> head; final P priority; Node( E entity, P priority ) { this.head = new Link<E>( entity, null ); this.priority = priority; } } private static class Link<E> { final E entity; final Link<E> next; Link( E entity, Link<E> next ) { this.entity = entity; this.next = next; } } }