Home  |  About  |  RSS antonfagerberg.com

Generic Skip List (map) in Java

18 May 2014

The skip list is a interesting probabilistic data structure which can be used as an alternative to trees. Below is a simple implementation in Java using generics to store key-value pairs and therefore works as a map.

import java.util.NoSuchElementException;
import java.util.Random;

public class SkipList<K extends Comparable<K>,V> {
    private QuadNode<K, V> head = new QuadNode<K, V>(null, null, 0);
    final static Random random = new Random();

    public void insert(K key, V value) {
        int level = 0;

        while (random.nextBoolean()) {
            level++;
        }

        while (head.getLevel() < level) {
            QuadNode<K, V> newHead = new QuadNode<K, V>(null, null, head.getLevel() + 1);
            head.setUp(newHead);
            newHead.setDown(head);
            head = newHead;
        }

        head.insert(key, value, level, null);
    }

    public V find(K key) {
        return head.find(key).getValue();
    }

    public void delete(K key) {
        for (QuadNode<K,V> node = head.find(key); node != null; node = node.getDown()) {
            node.getPrevious().setNext(node.getNext());
            if (node.getNext() != null) {
                node.getNext().setPrevious(node.getPrevious());
            }
        }

        while (head.getNext() == null) {
            head = head.getDown();
            head.setUp(null);
        }
    }

    @Override
    public String toString() {
        return head.toString();
    }

    public static void main(String[] args) {
        SkipList<Integer, String> sl = new SkipList<Integer, String>();
        sl.insert(3, "tre");
        sl.insert(6, "sex");
        sl.insert(2, "två");
        sl.insert(5, "fem");
        sl.insert(1, "ett");
        try {
            sl.insert(1, "ett");
            throw new IllegalStateException("Duplicates are not allowed!");
        } catch (IllegalArgumentException e) {
            System.out.println("Yes, 1 should not be allowed again.");
        }

        System.out.println(sl);
        System.out.println("-------");
        System.out.println(sl.find(3).equals("tre"));
        System.out.println(sl.find(6).equals("sex"));
        System.out.println(sl.find(2).equals("två"));
        System.out.println(sl.find(5).equals("fem"));
        System.out.println(sl.find(1).equals("ett"));

        sl.delete(6);
        System.out.println(sl);
        System.out.println("-------");

        sl.delete(3);
        System.out.println(sl);
        System.out.println("-------");

        try {
            sl.find(3);
            throw new IllegalStateException("Nooo!");
        } catch (NoSuchElementException e) {
            System.out.println("Yes, 3 should not be found");
        }
    }
}

import java.util.NoSuchElementException;

public class QuadNode<K extends Comparable<K>,V> {
    private QuadNode<K,V>
            up,
            down,
            next,
            previous;
    private K key;
    private V value;
    private int level;

    public QuadNode(K key, V value, int level) {
        this.key = key;
        this.value = value;
        this.level = level;
    }

    public void insert(K key, V value, int level, QuadNode<K,V> parent) {
        if (this.level <= level && (next == null || next.getKey().compareTo(key) > 0)) {
            QuadNode<K, V> newNode = new QuadNode<K, V>(key, value, this.level);

            if (next != null) {
                next.setPrevious(newNode);
                newNode.setNext(next);
            }

            next = newNode;
            newNode.setPrevious(this);

            if (parent != null) {
                newNode.setUp(parent);
                parent.setDown(newNode);
            }

            if (down != null) {
                down.insert(key, value, level, newNode);
            }
        } else if (next != null && next.getKey().compareTo(key) < 0) {
            next.insert(key, value, level, parent);
        } else if (next != null && next.getKey().compareTo(key) == 0) {
            throw new IllegalArgumentException("Duplicate key is not allowed!");
        } else if (down != null) {
            down.insert(key, value, level, parent);
        }
    }

    public QuadNode<K, V> find(K key) {
        if (next != null) {
            int compareResult = next.getKey().compareTo(key);

            if (compareResult == 0) {
                return next;
            } else if (compareResult < 0) {
                return next.find(key);
            } else if (down != null) {
                return down.find(key);
            } else {
                throw new NoSuchElementException();
            }
        } else if (down != null) {
            return down.find(key);
        } else {
            throw new NoSuchElementException();
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();

        QuadNode<K, V> bottom = this;

        while (bottom.getDown() != null) {
            bottom = bottom.getDown();
        }

        for (QuadNode<K, V> node = bottom; node != null; node = node.getUp()) {
            sb.append('[').append((node.getKey() == null) ? "H" : node.getKey().toString()).append(']');
        }

        if (bottom.next != null) {
            sb.append('\n').append(bottom.next.toString());
        }

        return sb.toString();
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public void setPrevious(QuadNode qn) {
        previous = qn;
    }

    public void setNext(QuadNode qn) {
        next = qn;
    }

    public void setDown(QuadNode qn) {
        down = qn;
    }

    public void setUp(QuadNode qn) {
        up = qn;
    }

    public QuadNode<K,V> getUp() {
        return up;
    }

    public QuadNode<K,V> getDown() {
        return down;
    }

    public QuadNode<K,V> getPrevious() {
        return previous;
    }

    public QuadNode<K,V> getNext() {
        return next;
    }

    public int getLevel() {
        return level;
    }
}

Code available on my GitHub page.