
org.neo4j.graphalgo.impl.util.PriorityMap Maven / Gradle / Ivy
Show all versions of neo4j-graph-algo Show documentation
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.graphalgo.impl.util;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
public class PriorityMap
{
public interface Converter
{
T convert( S source );
}
public static final class Entry
{
private final E entity;
private final P priority;
private Entry( E entity, P priority )
{
this.entity = entity;
this.priority = priority;
}
Entry( Node node )
{
this( node.head.entity, node.head.priority );
}
public E getEntity()
{
return entity;
}
public P getPriority()
{
return priority;
}
}
@SuppressWarnings( "rawtypes" )
private static final Converter SELF_KEY = new Converter()
{
@Override
public Object convert( Object source )
{
return source;
}
};
@SuppressWarnings( "unchecked" )
public static PriorityMap withSelfKey(
Comparator priority )
{
return new PriorityMap( SELF_KEY, priority, true );
}
private static class NaturalPriority> implements
Comparator
{
private final boolean reversed;
NaturalPriority( boolean reversed )
{
this.reversed = reversed;
}
@Override
public int compare( P o1, P o2 )
{
return reversed ? o2.compareTo( o1 ) : o1.compareTo( o2 );
}
}
public static > PriorityMap withNaturalOrder(
Converter key )
{
return PriorityMap.withNaturalOrder( key, false );
}
public static > PriorityMap withNaturalOrder(
Converter key, boolean reversed )
{
return withNaturalOrder( key, reversed, true );
}
public static > PriorityMap withNaturalOrder(
Converter key, boolean reversed, boolean onlyKeepBestPriorities )
{
Comparator priority = new NaturalPriority
( reversed );
return new PriorityMap( key, priority, onlyKeepBestPriorities );
}
public static > PriorityMap withSelfKeyNaturalOrder()
{
return PriorityMap.withSelfKeyNaturalOrder( false );
}
public static > PriorityMap withSelfKeyNaturalOrder(
boolean reversed )
{
return PriorityMap.withSelfKeyNaturalOrder( reversed, true );
}
@SuppressWarnings( "unchecked" )
public static > PriorityMap withSelfKeyNaturalOrder(
boolean reversed, boolean onlyKeepBestPriorities )
{
Comparator priority = new NaturalPriority
( reversed );
return new PriorityMap( SELF_KEY, priority, onlyKeepBestPriorities );
}
private final Converter keyFunction;
private final Comparator order;
private final boolean onlyKeepBestPriorities;
public PriorityMap( Converter key, Comparator priority, boolean onlyKeepBestPriorities )
{
this.keyFunction = key;
this.order = priority;
this.onlyKeepBestPriorities = onlyKeepBestPriorities;
}
/**
* 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 node = map.get( key );
boolean result = false;
if ( node != null )
{ // it already existed
if ( onlyKeepBestPriorities )
{
if ( order.compare( priority, node.head.priority ) == 0 )
{ // ...with same priority => add as a candidate first in chain
node.head = new Link( entity, priority, node.head );
result = true;
}
else if ( order.compare( priority, node.head.priority ) < 0 )
{ // ...with lower (better) priority => this new one replaces any existing
queue.remove( node );
putNew( entity, priority, key );
result = true;
}
}
else
{ // put in the appropriate place in the node linked list
if ( order.compare( priority, node.head.priority ) < 0 )
{ // ...first in chain and re-insert to queue
node.head = new Link( entity, priority, node.head );
reinsert( node );
result = true;
}
else
{ // we couldn't add it first in chain, go look for the appropriate place
Link link = node.head, prev = link;
// skip the first one since we already compared head
link = link.next;
while ( link != null )
{
if ( order.compare( priority, link.priority ) <= 0 )
{ // here's our place, put it
// NODE ==> N ==> N ==> N
prev.next = new Link( entity, priority, link );
result = true;
break;
}
prev = link;
link = link.next;
}
if ( !result )
{ // not added so append last in the chain
prev.next = new Link( entity, priority, null );
result = true;
}
}
}
}
else
{ // Didn't exist, just put
putNew( entity, priority, key );
result = true;
}
return result;
}
private void putNew( E entity, P priority, K key )
{
Node node = new Node( new Link( entity, priority, null ) );
map.put( key, node );
queue.add( node );
}
private void reinsert( Node node )
{
queue.remove( 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 node = map.get( key );
return node != null ? node.head.priority : null;
}
/**
* Remove and return the entry with the highest priority.
*
* @return the entry with the highest priority.
*/
public Entry pop()
{
Node node = queue.peek();
Entry result = null;
if ( node == null )
{
// Queue is empty
return null;
}
else if ( node.head.next == null )
{
// There are no more entries attached to this key
// Poll from queue and remove from map.
node = queue.poll();
map.remove( keyFunction.convert( node.head.entity ) );
result = new Entry( node );
}
else
{
result = new Entry( node );
node.head = node.head.next;
if ( order.compare( result.priority, node.head.priority ) == 0 )
{
// Can leave at front of queue as priority is the same
// Do nothing
}
else
{
// node needs to be reinserted into queue
reinsert( node );
}
}
return result;
}
public Entry peek()
{
Node node = queue.peek();
if ( node == null )
{
return null;
}
return new Entry( node );
}
// Naive implementation
private final Map> map = new HashMap>();
private final PriorityQueue> queue = new PriorityQueue>(
11, new Comparator>()
{
@Override
public int compare( Node o1, Node o2 )
{
return order.compare( o1.head.priority, o2.head.priority );
}
} );
private static class Node
{
private Link head;
Node( Link head )
{
this.head = head;
}
}
private static class Link
{
private final E entity;
private final P priority;
private Link next;
Link( E entity, P priority, Link next )
{
this.entity = entity;
this.priority = priority;
this.next = next;
}
}
}