org.apache.hadoop.util.IntrusiveCollection.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.util.IntrusiveCollection.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.hadoop.util;

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;

import com.google.common.base.Preconditions;

/**
 * Implements an intrusive doubly-linked list.
 *
 * An intrusive linked list is one in which the elements themselves are
 * responsible for storing the pointers to previous and next elements.
 * This can save a lot of memory if there are many elements in the list or
 * many lists.
 */
@InterfaceAudience.Private
public class IntrusiveCollection<E extends IntrusiveCollection.Element> implements Collection<E> {
    /**
     * An element contained in this list.
     *
     * We pass the list itself as a parameter so that elements can belong to
     * multiple lists.  (The element will need to store separate prev and next
     * pointers for each.)
     */
    @InterfaceAudience.Private
    public interface Element {
        /**
         * Insert this element into the list.  This is the first thing that will
         * be called on the element.
         */
        void insertInternal(IntrusiveCollection<? extends Element> list, Element prev, Element next);

        /**
         * Set the prev pointer of an element already in the list.
         */
        void setPrev(IntrusiveCollection<? extends Element> list, Element prev);

        /**
         * Set the next pointer of an element already in the list.
         */
        void setNext(IntrusiveCollection<? extends Element> list, Element next);

        /**
         * Remove an element from the list.  This is the last thing that will be
         * called on an element.
         */
        void removeInternal(IntrusiveCollection<? extends Element> list);

        /**
         * Get the prev pointer of an element.
         */
        Element getPrev(IntrusiveCollection<? extends Element> list);

        /**
         * Get the next pointer of an element.
         */
        Element getNext(IntrusiveCollection<? extends Element> list);

        /**
         * Returns true if this element is in the provided list.
         */
        boolean isInList(IntrusiveCollection<? extends Element> list);
    }

    private Element root = new Element() {
        // We keep references to the first and last elements for easy access.
        Element first = this;
        Element last = this;

        @Override
        public void insertInternal(IntrusiveCollection<? extends Element> list, Element prev, Element next) {
            throw new RuntimeException("Can't insert root element");
        }

        @Override
        public void setPrev(IntrusiveCollection<? extends Element> list, Element prev) {
            Preconditions.checkState(list == IntrusiveCollection.this);
            last = prev;
        }

        @Override
        public void setNext(IntrusiveCollection<? extends Element> list, Element next) {
            Preconditions.checkState(list == IntrusiveCollection.this);
            first = next;
        }

        @Override
        public void removeInternal(IntrusiveCollection<? extends Element> list) {
            throw new RuntimeException("Can't remove root element");
        }

        @Override
        public Element getNext(IntrusiveCollection<? extends Element> list) {
            Preconditions.checkState(list == IntrusiveCollection.this);
            return first;
        }

        @Override
        public Element getPrev(IntrusiveCollection<? extends Element> list) {
            Preconditions.checkState(list == IntrusiveCollection.this);
            return last;
        }

        @Override
        public boolean isInList(IntrusiveCollection<? extends Element> list) {
            return list == IntrusiveCollection.this;
        }

        @Override
        public String toString() {
            return "root"; // + IntrusiveCollection.this + "]";
        }
    };

    private int size = 0;

    /**
     * An iterator over the intrusive collection.
     *
     * Currently, you can remove elements from the list using
     * #{IntrusiveIterator#remove()}, but modifying the collection in other
     * ways during the iteration is not supported.
     */
    public class IntrusiveIterator implements Iterator<E> {
        Element cur;
        Element next;

        IntrusiveIterator() {
            this.cur = root;
            this.next = null;
        }

        @Override
        public boolean hasNext() {
            if (next == null) {
                next = cur.getNext(IntrusiveCollection.this);
            }
            return next != root;
        }

        @SuppressWarnings("unchecked")
        @Override
        public E next() {
            if (next == null) {
                next = cur.getNext(IntrusiveCollection.this);
            }
            if (next == root) {
                throw new NoSuchElementException();
            }
            cur = next;
            next = null;
            return (E) cur;
        }

        @Override
        public void remove() {
            if (cur == null) {
                throw new IllegalStateException("Already called remove " + "once on this element.");
            }
            next = removeElement(cur);
            cur = null;
        }
    }

    private Element removeElement(Element elem) {
        Element prev = elem.getPrev(IntrusiveCollection.this);
        Element next = elem.getNext(IntrusiveCollection.this);
        elem.removeInternal(IntrusiveCollection.this);
        prev.setNext(IntrusiveCollection.this, next);
        next.setPrev(IntrusiveCollection.this, prev);
        size--;
        return next;
    }

    /**
     * Get an iterator over the list.  This can be used to remove elements.
     * It is not safe to do concurrent modifications from other threads while
     * using this iterator.
     * 
     * @return         The iterator.
     */
    public Iterator<E> iterator() {
        return new IntrusiveIterator();
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean contains(Object o) {
        try {
            Element element = (Element) o;
            return element.isInList(this);
        } catch (ClassCastException e) {
            return false;
        }
    }

    @Override
    public Object[] toArray() {
        Object ret[] = new Object[size];
        int i = 0;
        for (Iterator<E> iter = iterator(); iter.hasNext();) {
            ret[i++] = iter.next();
        }
        return ret;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T[] toArray(T[] array) {
        if (array.length < size) {
            return (T[]) toArray();
        } else {
            int i = 0;
            for (Iterator<E> iter = iterator(); iter.hasNext();) {
                array[i++] = (T) iter.next();
            }
        }
        return array;
    }

    /**
     * Add an element to the end of the list.
     * 
     * @param elem     The new element to add.
     */
    @Override
    public boolean add(E elem) {
        if (elem == null) {
            return false;
        }
        if (elem.isInList(this)) {
            return false;
        }
        Element prev = root.getPrev(IntrusiveCollection.this);
        prev.setNext(IntrusiveCollection.this, elem);
        root.setPrev(IntrusiveCollection.this, elem);
        elem.insertInternal(IntrusiveCollection.this, prev, root);
        size++;
        return true;
    }

    /**
     * Add an element to the front of the list.
     *
     * @param elem     The new element to add.
     */
    public boolean addFirst(Element elem) {
        if (elem == null) {
            return false;
        }
        if (elem.isInList(this)) {
            return false;
        }
        Element next = root.getNext(IntrusiveCollection.this);
        next.setPrev(IntrusiveCollection.this, elem);
        root.setNext(IntrusiveCollection.this, elem);
        elem.insertInternal(IntrusiveCollection.this, root, next);
        size++;
        return true;
    }

    public static final Log LOG = LogFactory.getLog(IntrusiveCollection.class);

    @Override
    public boolean remove(Object o) {
        try {
            Element elem = (Element) o;
            if (!elem.isInList(this)) {
                return false;
            }
            removeElement(elem);
            return true;
        } catch (ClassCastException e) {
            return false;
        }
    }

    @Override
    public boolean containsAll(Collection<?> collection) {
        for (Object o : collection) {
            if (!contains(o)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends E> collection) {
        boolean changed = false;
        for (E elem : collection) {
            if (add(elem)) {
                changed = true;
            }
        }
        return changed;
    }

    @Override
    public boolean removeAll(Collection<?> collection) {
        boolean changed = false;
        for (Object elem : collection) {
            if (remove(elem)) {
                changed = true;
            }
        }
        return changed;
    }

    @Override
    public boolean retainAll(Collection<?> collection) {
        boolean changed = false;
        for (Iterator<E> iter = iterator(); iter.hasNext();) {
            Element elem = iter.next();
            if (!collection.contains(elem)) {
                iter.remove();
                changed = true;
            }
        }
        return changed;
    }

    /**
     * Remove all elements.
     */
    @Override
    public void clear() {
        for (Iterator<E> iter = iterator(); iter.hasNext();) {
            iter.next();
            iter.remove();
        }
    }
}