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

com.fluxtion.agrona.collections.LongArrayList Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2024 Real Logic Limited.
 *
 * Licensed 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
 *
 * https://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 com.fluxtion.agrona.collections;

import com.fluxtion.agrona.generation.DoNotSub;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.RandomAccess;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
import java.util.function.LongPredicate;
import java.util.stream.LongStream;

import static java.util.Objects.requireNonNull;

/**
 * A {@link List} implementation that stores long values with the ability to not have them boxed.
 */
public class LongArrayList extends AbstractList implements List, RandomAccess
{
    /**
     * The default value that will be used in place of null for an element.
     */
    public static final long DEFAULT_NULL_VALUE = Long.MIN_VALUE;

    /**
     * Initial capacity to which the array will be sized.
     */
    @DoNotSub public static final int INITIAL_CAPACITY = 10;

    private final long nullValue;
    @DoNotSub private int size = 0;
    private long[] elements;

    /**
     * Constructs a new list with the {@link #INITIAL_CAPACITY} using {@link #DEFAULT_NULL_VALUE}.
     */
    public LongArrayList()
    {
        this(INITIAL_CAPACITY, DEFAULT_NULL_VALUE);
    }

    /**
     * Construct a new list.
     *
     * @param initialCapacity for the backing array.
     * @param nullValue       to be used to represent a null element.
     */
    public LongArrayList(
        @DoNotSub final int initialCapacity,
        final long nullValue)
    {
        this.nullValue = nullValue;
        elements = new long[Math.max(initialCapacity, INITIAL_CAPACITY)];
    }

    /**
     * Create a new list that wraps an existing arrays without copying it.
     *
     * @param initialElements to be wrapped.
     * @param initialSize     of the array to wrap.
     * @param nullValue       to be used to represent a null element.
     */
    @SuppressWarnings("this-escape")
    public LongArrayList(
        final long[] initialElements,
        @DoNotSub final int initialSize,
        final long nullValue)
    {
        wrap(initialElements, initialSize);
        this.nullValue = nullValue;
    }

    /**
     * Wrap an existing array without copying it.
     *
     * @param initialElements to be wrapped.
     * @param initialSize     of the array to wrap.
     * @throws IllegalArgumentException if the initialSize is less than 0 or greater than the length of the
     *                                  initial array.
     */
    public void wrap(
        final long[] initialElements,
        final @DoNotSub int initialSize)
    {
        if (initialSize < 0 || initialSize > initialElements.length)
        {
            throw new IllegalArgumentException(
                "illegal initial size " + initialSize + " for array length of " + initialElements.length);
        }

        elements = initialElements;
        size = initialSize;
    }

    /**
     * The value representing a null element.
     *
     * @return value representing a null element.
     */
    public long nullValue()
    {
        return nullValue;
    }

    /**
     * {@inheritDoc}
     */
    @DoNotSub public int size()
    {
        return size;
    }

    /**
     * The current capacity for the collection.
     *
     * @return the current capacity for the collection.
     */
    @DoNotSub public int capacity()
    {
        return elements.length;
    }

    /**
     * {@inheritDoc}
     */
    public void clear()
    {
        size = 0;
    }

    /**
     * Trim the underlying array to be the current size, or {@link #INITIAL_CAPACITY} if size is less.
     */
    public void trimToSize()
    {
        if (elements.length != size && elements.length > INITIAL_CAPACITY)
        {
            elements = Arrays.copyOf(elements, Math.max(INITIAL_CAPACITY, size));
        }
    }

    /**
     * {@inheritDoc}
     */
    public Long get(
        @DoNotSub final int index)
    {
        final long value = getLong(index);

        return nullValue == value ? null : value;
    }

    /**
     * Get the element at a given index without boxing.
     *
     * @param index to get.
     * @return the unboxed element.
     */
    public long getLong(
        @DoNotSub final int index)
    {
        checkIndex(index);

        return elements[index];
    }

    /**
     * {@inheritDoc}
     */
    public boolean add(final Long element)
    {
        return addLong(null == element ? nullValue : element);
    }

    /**
     * Add an element without boxing.
     *
     * @param element to be added.
     * @return true
     */
    public boolean addLong(final long element)
    {
        ensureCapacityPrivate(size + 1);

        elements[size] = element;
        size++;

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public void add(
        @DoNotSub final int index,
        final Long element)
    {
        addLong(index, null == element ? nullValue : element);
    }

    /**
     * Add an element without boxing at a given index.
     *
     * @param index   at which the element should be added.
     * @param element to be added.
     */
    public void addLong(
        @DoNotSub final int index,
        final long element)
    {
        checkIndexForAdd(index);

        @DoNotSub final int requiredSize = size + 1;
        ensureCapacityPrivate(requiredSize);

        if (index < size)
        {
            System.arraycopy(elements, index, elements, index + 1, size - index);
        }

        elements[index] = element;
        size++;
    }

    /**
     * {@inheritDoc}
     */
    public Long set(
        @DoNotSub final int index,
        final Long element)
    {
        final long previous = setLong(index, null == element ? nullValue : element);

        return nullValue == previous ? null : previous;
    }

    /**
     * Set an element at a given index without boxing.
     *
     * @param index   at which to set the element.
     * @param element to be added.
     * @return the previous element at the index.
     */
    public long setLong(
        @DoNotSub final int index,
        final long element)
    {
        checkIndex(index);

        final long previous = elements[index];
        elements[index] = element;

        return previous;
    }

    /**
     * {@inheritDoc}
     */
    public boolean contains(final Object o)
    {
        return containsLong(null == o ? nullValue : (long)o);
    }

    /**
     * Does the list contain this element value.
     *
     * @param value of the element.
     * @return true if present otherwise false.
     */
    public boolean containsLong(final long value)
    {
        return -1 != indexOf(value);
    }

    /**
     * Index of the first element with this value.
     *
     * @param value for the element.
     * @return the index if found otherwise -1.
     */
    public @DoNotSub int indexOf(
        final long value)
    {
        final long[] elements = this.elements;
        for (@DoNotSub int i = 0, size = this.size; i < size; i++)
        {
            if (value == elements[i])
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * Index of the last element with this value.
     *
     * @param value for the element.
     * @return the index if found otherwise -1.
     */
    public @DoNotSub int lastIndexOf(
        final long value)
    {
        final long[] elements = this.elements;
        for (@DoNotSub int i = size - 1; i >= 0; i--)
        {
            if (value == elements[i])
            {
                return i;
            }
        }

        return -1;
    }

    /**
     * Appends all the elements in the specified list to the end of this list, in the order that they are stored in the
     * specified list.
     *
     * @param list containing elements to be added to this list.
     * @return {@code true} if this list changed as a result of the call.
     */
    public boolean addAll(final LongArrayList list)
    {
        @DoNotSub final int numElements = list.size;
        if (numElements > 0)
        {
            ensureCapacityPrivate(size + numElements);
            System.arraycopy(list.elements, 0, elements, size, numElements);
            size += numElements;
            return true;
        }
        return false;
    }

    /**
     * Inserts all the elements from the specified list to this list at the specified position. Shifts the element
     * currently at that position (if any) and any subsequent elements to the right (increases their indices). The new
     * elements will appear in this list in the order that they are stored in the specified list.
     *
     * @param index at which to insert the first element from the specified collection.
     * @param list  containing elements to be added to this list.
     * @return {@code true} if this list changed as a result of the call.
     */
    public boolean addAll(
        @DoNotSub final int index,
        final LongArrayList list)
    {
        checkIndexForAdd(index);

        @DoNotSub final int numElements = list.size;
        if (numElements > 0)
        {
            @DoNotSub final int size = this.size;
            ensureCapacityPrivate(size + numElements);
            final long[] elements = this.elements;
            for (@DoNotSub int i = size - 1; i >= index; i--)
            {
                elements[i + numElements] = elements[i];
            }
            System.arraycopy(list.elements, 0, elements, index, numElements);
            this.size += numElements;
            return true;
        }
        return false;
    }

    /**
     * Returns {@code true} if this list contains all the elements of the specified list.
     *
     * @param list to be checked for containment in this list.
     * @return {@code true} if this list contains all the elements of the specified list.
     */
    public boolean containsAll(final LongArrayList list)
    {
        final long[] listElements = list.elements;
        final long listNullValue = list.nullValue;
        final boolean hasNulls = contains(null);
        for (@DoNotSub int i = 0, size = list.size; i < size; i++)
        {
            final long value = listElements[i];
            if (!(containsLong(value) || hasNulls && listNullValue == value))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Retains only the elements in this list that are contained in the specified list. In other words, removes from
     * this list all of its elements that are not contained in the specified list.
     *
     * @param list containing elements to be removed from this list.
     * @return {@code true} if this list changed as a result of the call.
     */
    public boolean retainAll(final LongArrayList list)
    {
        final long[] elements = this.elements;
        @DoNotSub final int size = this.size;
        if (size > 0)
        {
            if (list.isEmpty())
            {
                this.size = 0;
                return true;
            }

            final long nullValue = this.nullValue;
            final boolean listHasNulls = list.contains(null);
            long[] filteredElements = null;
            @DoNotSub int j = -1;
            for (@DoNotSub int i = 0; i < size; i++)
            {
                final long value = elements[i];
                if (!(list.containsLong(value) || (listHasNulls && nullValue == value)))
                {
                    if (null == filteredElements)
                    {
                        filteredElements = Arrays.copyOf(elements, size);
                        j = i - 1;
                    }
                }
                else if (null != filteredElements)
                {
                    filteredElements[++j] = value;
                }
            }

            if (null != filteredElements)
            {
                this.elements = filteredElements;
                this.size = j + 1;
                return true;
            }
        }
        return false;
    }

    /**
     * Removes all of this collection's elements that are also contained in the specified list. After this call
     * returns, this list will contain no elements in common with the specified list.
     *
     * @param list whose elements are to be removed from this list.
     * @return {@code true} if this list changed as a result of the call.
     */
    public boolean removeAll(final LongArrayList list)
    {
        final long[] elements = this.elements;
        @DoNotSub final int size = this.size;
        if (size > 0 && !list.isEmpty())
        {
            final long nullValue = this.nullValue;
            final boolean listHasNulls = list.contains(null);
            long[] filteredElements = null;
            @DoNotSub int j = -1;
            for (@DoNotSub int i = 0; i < size; i++)
            {
                final long value = elements[i];
                if (list.containsLong(value) || (listHasNulls && nullValue == value))
                {
                    if (null == filteredElements)
                    {
                        filteredElements = Arrays.copyOf(elements, size);
                        j = i - 1;
                    }
                }
                else if (null != filteredElements)
                {
                    filteredElements[++j] = value;
                }
            }

            if (null != filteredElements)
            {
                this.elements = filteredElements;
                this.size = j + 1;
                return true;
            }
        }
        return false;
    }

    /**
     * Removes all the elements of this collection that satisfy the given predicate.
     *
     * @param filter a predicate which returns true for elements to be removed.
     * @return {@code true} if any elements were removed.
     */
    public boolean removeIfLong(final LongPredicate filter)
    {
        requireNonNull(filter);
        final long[] elements = this.elements;
        @DoNotSub final int size = this.size;
        if (size > 0)
        {
            long[] filteredElements = null;
            @DoNotSub int j = -1;
            for (@DoNotSub int i = 0; i < size; i++)
            {
                final long value = elements[i];
                if (filter.test(value))
                {
                    if (null == filteredElements)
                    {
                        filteredElements = Arrays.copyOf(elements, size);
                        j = i - 1;
                    }
                }
                else if (null != filteredElements)
                {
                    filteredElements[++j] = value;
                }
            }

            if (null != filteredElements)
            {
                this.elements = filteredElements;
                this.size = j + 1;
                return true;
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public boolean remove(final Object o)
    {
        return removeLong(null == o ? nullValue : (long)o);
    }

    /**
     * Remove at a given index.
     *
     * @param index of the element to be removed.
     * @return the existing value at this index.
     */
    public Long remove(
        @DoNotSub final int index)
    {
        final long value = removeAt(index);
        return nullValue == value ? null : value;
    }

    /**
     * Remove at a given index.
     *
     * @param index of the element to be removed.
     * @return the existing value at this index.
     */
    public long removeAt(
        @DoNotSub final int index)
    {
        checkIndex(index);

        final long value = elements[index];

        @DoNotSub final int moveCount = size - index - 1;
        if (moveCount > 0)
        {
            System.arraycopy(elements, index + 1, elements, index, moveCount);
        }

        size--;

        return value;
    }

    /**
     * Removes element at index, but instead of copying all elements to the left,
     * it replaces the item in the slot with the last item in the list. This avoids the copy
     * costs at the expense of preserving list order. If index is the last element it is just removed.
     *
     * @param index of the element to be removed.
     * @return the existing value at this index.
     * @throws IndexOutOfBoundsException if index is out of bounds.
     */
    public long fastUnorderedRemove(
        @DoNotSub final int index)
    {
        checkIndex(index);

        final long value = elements[index];
        elements[index] = elements[--size];

        return value;
    }

    /**
     * Remove the first instance of a value if found in the list.
     * 

* Primitive specialization of the {@link List#remove(Object)} method. * * @param value to be removed. * @return true if successful otherwise false. */ public boolean removeLong(final long value) { @DoNotSub final int index = indexOf(value); if (-1 != index) { removeAt(index); return true; } return false; } /** * Remove the first instance of a value if found in the list and replaces it with the last item * in the list. This saves a copy down of all items at the expense of not preserving list order. * * @param value to be removed. * @return true if successful otherwise false. */ public boolean fastUnorderedRemoveLong(final long value) { @DoNotSub final int index = indexOf(value); if (-1 != index) { elements[index] = elements[--size]; return true; } return false; } /** * Push an element onto the end of the array like a stack. * * @param element to be pushed onto the end of the array. */ public void pushLong(final long element) { ensureCapacityPrivate(size + 1); elements[size] = element; size++; } /** * Pop a value off the end of the array as a stack operation. * * @return the value at the end of the array. * @throws NoSuchElementException if the array is empty. */ public long popLong() { if (isEmpty()) { throw new NoSuchElementException(); } return elements[--size]; } /** * For each element in order provide the long value to a {@link LongConsumer}. * * @param action to be taken for each element. */ public void forEachOrderedLong(final LongConsumer action) { final long[] elements = this.elements; for (@DoNotSub int i = 0, size = this.size; i < size; i++) { action.accept(elements[i]); } } /** * Create a {@link LongStream} over the elements of underlying array. * * @return a {@link LongStream} over the elements of underlying array. */ public LongStream longStream() { return Arrays.stream(elements, 0, size); } /** * Create a new array that is a copy of the elements. * * @return a copy of the elements. */ public long[] toLongArray() { return Arrays.copyOf(elements, size); } /** * Create a new array that is a copy of the elements. * * @param dst destination array for the copy if it is the correct size. * @return a copy of the elements. */ public long[] toLongArray(final long[] dst) { if (dst.length == size) { System.arraycopy(elements, 0, dst, 0, dst.length); return dst; } else { return Arrays.copyOf(elements, size); } } /** * Ensure the backing array has a required capacity. * * @param requiredCapacity for the backing array. */ public void ensureCapacity(@DoNotSub final int requiredCapacity) { ensureCapacityPrivate(Math.max(requiredCapacity, INITIAL_CAPACITY)); } /** * Type-safe overload of the {@link #equals(Object)} method. * * @param that other list. * @return {@code true} if lists are equal. */ public boolean equals(final LongArrayList that) { if (that == this) { return true; } boolean isEqual = false; @DoNotSub final int size = this.size; if (size == that.size) { isEqual = true; final long[] elements = this.elements; final long[] thatElements = that.elements; for (@DoNotSub int i = 0; i < size; i++) { final long thisValue = elements[i]; final long thatValue = thatElements[i]; if (thisValue != thatValue) { if (thisValue != this.nullValue || thatValue != that.nullValue) { isEqual = false; break; } } } } return isEqual; } /** * {@inheritDoc} */ public boolean equals(final Object other) { if (other == this) { return true; } boolean isEqual = false; if (other instanceof LongArrayList) { return equals((LongArrayList)other); } else if (other instanceof List) { final List that = (List)other; if (size == that.size()) { isEqual = true; @DoNotSub int i = 0; for (final Object o : that) { if (null == o || o instanceof Long) { final Long thisValue = get(i++); final Long thatValue = (Long)o; if (Objects.equals(thisValue, thatValue)) { continue; } } isEqual = false; break; } } } return isEqual; } /** * {@inheritDoc} */ @DoNotSub public int hashCode() { @DoNotSub int hashCode = 1; final long nullValue = this.nullValue; final long[] elements = this.elements; for (@DoNotSub int i = 0, size = this.size; i < size; i++) { final long value = elements[i]; hashCode = 31 * hashCode + (nullValue == value ? 0 : Long.hashCode(value)); } return hashCode; } /** * {@inheritDoc} */ public void forEach(final Consumer action) { requireNonNull(action); final long nullValue = this.nullValue; final long[] elements = this.elements; for (@DoNotSub int i = 0, size = this.size; i < size; i++) { final long value = elements[i]; action.accept(nullValue != value ? value : null); } } /** * Iterate over the collection without boxing. * * @param action to be taken for each element. */ public void forEachLong(final LongConsumer action) { requireNonNull(action); final long[] elements = this.elements; for (@DoNotSub int i = 0, size = this.size; i < size; i++) { action.accept(elements[i]); } } /** * {@inheritDoc} */ public String toString() { final StringBuilder sb = new StringBuilder(); sb.append('['); final long nullValue = this.nullValue; final long[] elements = this.elements; for (@DoNotSub int i = 0, size = this.size; i < size; i++) { final long value = elements[i]; sb.append(value != nullValue ? value : null).append(", "); } if (sb.length() > 1) { sb.setLength(sb.length() - 2); } sb.append(']'); return sb.toString(); } private void ensureCapacityPrivate(@DoNotSub final int requiredCapacity) { @DoNotSub final int currentCapacity = elements.length; if (requiredCapacity > currentCapacity) { if (requiredCapacity > ArrayUtil.MAX_CAPACITY) { throw new IllegalStateException("max capacity: " + ArrayUtil.MAX_CAPACITY); } @DoNotSub int newCapacity = currentCapacity > INITIAL_CAPACITY ? currentCapacity : INITIAL_CAPACITY; while (newCapacity < requiredCapacity) { newCapacity = newCapacity + (newCapacity >> 1); if (newCapacity < 0 || newCapacity >= ArrayUtil.MAX_CAPACITY) { newCapacity = ArrayUtil.MAX_CAPACITY; } } final long[] newElements = new long[newCapacity]; System.arraycopy(elements, 0, newElements, 0, currentCapacity); elements = newElements; } } private void checkIndex(@DoNotSub final int index) { if (index >= size || index < 0) { throw new IndexOutOfBoundsException("index=" + index + " size=" + size); } } private void checkIndexForAdd(@DoNotSub final int index) { if (index > size || index < 0) { throw new IndexOutOfBoundsException("index=" + index + " size=" + size); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy