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

net.algart.arrays.AbstractObjectArray Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

There is a newer version: 1.4.23
Show newest version
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.arrays;

import java.util.Objects;

/**
 * 

Implementation of almost all basic functions of {@link ObjectArray} interface. * The only {@link ObjectArray#get(long)} method is not defined in this class; * all other methods are implemented via calls of {@link ObjectArray#get(long)}.

* * @param the generic type of array elements. * @author Daniel Alievsky */ public abstract class AbstractObjectArray extends AbstractArray implements ObjectArray { final boolean underlyingArraysAreParallel; final Class elementType; /** * Creates an array with the given initial capacity and length. * *

The underlyingArraysAreParallel informs whether the passed underlying arrays (if they exist) * are "parallel" to this one and to each other. * Intuitively, it means that every element #k of this array is connected (for example, depends on) * the elements #k (of, maybe, #k±i, where i is little) of the * underlying arrays. * Precisely, this argument affects the following in this implementation: * *

    *
  1. If it is true, then all passed underlying arrays * if underlyingArrays.length>1) * must have identical length — in other case, this constructor throws {@link SizeMismatchException}. *
  2. * *
  3. If it is true, then
      *
    • {@link #loadResources(ArrayContext context, long fromIndex, long toIndex)},
    • *
    • {@link #flushResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting)} * and
    • *
    • {@link #freeResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting)} *
    • *
    methods call * {@link #loadResources(ArrayContext context)}, * {@link #flushResources(ArrayContext context, boolean forcePhysicalWriting)} and * {@link #freeResources(ArrayContext context, boolean forcePhysicalWriting)} * methods for the corresponding {@link #subArray subarrays} of all underlying * arrays. If this argument is false, then
      *
    • {@link #loadResources(ArrayContext context, long fromIndex, long toIndex)} does nothing,
    • *
    • {@link #flushResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting)} * and {@link #freeResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting)} * methods ignore their fromIndex / toIndex arguments and call * {@link #loadResources(ArrayContext context)}, * {@link #flushResources(ArrayContext context, boolean forcePhysicalWriting)} and * {@link #freeResources(ArrayContext context, boolean forcePhysicalWriting)} * methods for original underlying arrays (not their subarrays).
    • *
    Of course, if you specify underlyingArraysAreParallel=false, you can override * {@link #loadResources(ArrayContext context, long fromIndex, long toIndex)}, * {@link #flushResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting)} * and {@link #freeResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting)} * methods and offer better implementations for subarrays, * for example, that will work with suitable regions of the underlying arrays. *
  4. *
* *

This array is not {@link #isNew() new} by default. * This is correct usually, because this class is often used * for creating a view of another data. However, if the instance * of this class does not depend on any other data sources, * you may call {@link #setNewStatus(boolean) setNewStatus(true)} in * the constructor of your subclass. * *

The created array never has new-read-only-view status: * {@link Array#isNewReadOnlyView()} method always returns false in this class and its inheritors. * * @param elementType the {@link #elementType() element type} of this array. * @param initialCapacity initial capacity of the array. * @param initialLength initial length of the array. * @param underlyingArraysAreParallel whether the underlying arrays are "parallel" to this. * @param underlyingArrays see the same argument of * {@link AbstractArray#AbstractArray(long, long, Array...)}. * @throws NullPointerException if underlyingArrays argument or some * of underlyingArrays[k] * elements is {@code null}. * @throws IllegalArgumentException if the initialCapacity or initialLength arguments * are illegal (negative, or capacity < length). * @throws SizeMismatchException if underlyingArraysAreParallel=true, * underlyingArrays.length>1 and some of passed arrays * have different lengths. */ protected AbstractObjectArray( Class elementType, long initialCapacity, long initialLength, boolean underlyingArraysAreParallel, Array... underlyingArrays) { super(initialCapacity, initialLength, underlyingArrays); Objects.requireNonNull(elementType, "Null elementType argument"); if (initialLength < 0) { throw new IllegalArgumentException("Negative initialLength argument"); } if (initialCapacity < 0) { throw new IllegalArgumentException("Negative initialCapacity argument"); } if (initialLength > initialCapacity) { throw new IllegalArgumentException("initialCapacity argument must not be less than initialLength"); } Objects.requireNonNull(underlyingArrays, "Null underlyingArrays argument"); this.underlyingArraysAreParallel = underlyingArraysAreParallel; long len = -1; for (int k = 0; k < underlyingArrays.length; k++) { Objects.requireNonNull(underlyingArrays[k], "Null underlyingArrays[" + k + "] argument"); if (underlyingArraysAreParallel) { if (k == 0) { len = underlyingArrays[k].length(); } else if (underlyingArrays[k].length() != len) { throw new SizeMismatchException("underlyingArrays[" + k + "].length() and underlyingArrays[0].length() mismatch"); } } } this.elementType = elementType; } /** * Equivalent to the constructor {@link #AbstractObjectArray(Class, long, long, boolean, Array...)}, * where both initialCapacity and initialLength arguments are equal to * initialCapacityAndLength. * * @param elementType the {@link #elementType() element type} of this array. * @param initialCapacityAndLength initial capacity and length of the array. * @param underlyingArraysAreParallel see {@link * #AbstractObjectArray(Class, long, long, boolean, Array...)}. * @param underlyingArrays see {@link * #AbstractObjectArray(Class, long, long, boolean, Array...)}. * @throws NullPointerException if underlyingArrays argument or some * of underlyingArrays[k] * elements is {@code null}. * @throws IllegalArgumentException if initialCapacityAndLength argument is negative. * @throws SizeMismatchException if underlyingArraysAreParallel=true, * underlyingArrays.length>1 and some of passed arrays * have different lengths. */ protected AbstractObjectArray( Class elementType, long initialCapacityAndLength, boolean underlyingArraysAreParallel, Array... underlyingArrays) { this(elementType, initialCapacityAndLength, initialCapacityAndLength, underlyingArraysAreParallel, underlyingArrays); } @Override public Class elementType() { return elementType; } @Override public Class> type() { return InternalUtils.cast(ObjectArray.class); } @Override public Class> updatableType() { return InternalUtils.cast(UpdatableObjectArray.class); } @Override public Class> mutableType() { return InternalUtils.cast(MutableObjectArray.class); } /** * This implementation is based on a loop of calls of {@link #get(long)} method. * Please override this method if it's possible to perform the same task more efficiently * than such a loop. * * @param arrayPos starting position in this AlgART array. * @param destArray the target Java array. * @param destArrayOffset starting position in the target Java array. * @param count the number of elements to be copied. * @throws NullPointerException if destArray argument is {@code null}. * @throws IllegalArgumentException if destArray argument is not an array. * @throws IndexOutOfBoundsException if copying would cause access of data outside this array or target array. * @throws ArrayStoreException if destArray element type mismatches with this array * {@link #elementType()}. * @throws ClassCastException if destArray element type mismatches with this array * {@link #elementType()} * (both this and ArrayStoreException are possible, * depending on implementation). */ @Override public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) { Objects.requireNonNull(destArray, "Null destArray argument"); Object[] a = (Object[]) destArray; if (count < 0) { throw new IllegalArgumentException("Negative number of loaded elements (" + count + ")"); } if (arrayPos < 0) { throw rangeException(arrayPos); } if (arrayPos > length - count) { throw rangeException(arrayPos + count - 1); } for (long arrayPosMax = arrayPos + count; arrayPos < arrayPosMax; arrayPos++, destArrayOffset++) { a[destArrayOffset] = get(arrayPos); } } /** * This implementation calls {@link #getData(long, Object, int, int)} * with corresponding arguments. * * @param arrayPos starting position in this AlgART array. * @param destArray the target Java array. * @throws NullPointerException if destArray argument is {@code null}. * @throws IllegalArgumentException if destArray argument is not an array. * @throws IndexOutOfBoundsException if arrayPos is out of range 0..length()-1. * @throws ArrayStoreException if destArray element type mismatches with this array * {@link #elementType()}. * @throws ClassCastException if destArray element type mismatches with this array * {@link #elementType()} * (both this and ArrayStoreException are possible, * depending on implementation). */ @Override public void getData(long arrayPos, Object destArray) { Objects.requireNonNull(destArray, "Null destArray argument"); if (arrayPos < 0 || arrayPos > length) { throw rangeException(arrayPos); } int count = ((Object[]) destArray).length; if (count > length - arrayPos) { count = (int) (length - arrayPos); } getData(arrayPos, destArray, 0, count); } /** * This implementation returns get(index). * * @param index index of element to get. * @return the element at the specified position in this array. * @throws IndexOutOfBoundsException if index is out of range 0..length()-1. */ @Override public Object getElement(long index) { return get(index); } public ObjectArray cast(Class elementType) { Class desiredType = InternalUtils.cast(elementType); if (!desiredType.isAssignableFrom(this.elementType)) { throw new ClassCastException("Illegal desired element type " + elementType + " for " + this); } return InternalUtils.cast(this); } /** * This implementation returns new instance of {@link AbstractObjectArray} with the same memory model * and underlying arrays, that were passed to the constructor of this instance, * and with overridden methods {@link #get(long)} and * {@link #getData(long arrayPos, Object destArray, int destArrayOffset, int count)}, * calling the same methods of this instance with corresponding corrections of the arguments. * *

The returned instance also have overridden methods {@link #loadResources(ArrayContext, long, long)}, * {@link #flushResources(ArrayContext, long, long, boolean)} and * {@link #freeResources(ArrayContext, long, long, boolean)}, * that also call the same methods of this instance with corresponding correction of their fromIndex * argument. * *

The returned instance also have overridden method {@link #isLazy()}, * that just calls the same methods of this instance with the same arguments. * * @param fromIndex low endpoint (inclusive) of the subarray. * @param toIndex high endpoint (exclusive) of the subarray. * @return a view of the specified range within this array. * @throws IndexOutOfBoundsException for illegal fromIndex and toIndex * (fromIndex < 0 || toIndex > length() || fromIndex > toIndex). */ @Override public Array subArray(long fromIndex, long toIndex) { checkSubArrayArguments(fromIndex, toIndex); final AbstractObjectArray parent = this; final long offset = fromIndex; return new AbstractObjectArray<>(elementType, toIndex - fromIndex, underlyingArraysAreParallel, underlyingArrays) { @Override public E get(long index) { if (index < 0 || index >= length) { throw rangeException(index); } return parent.get(offset + index); } @Override public long indexOf(long lowIndex, long highIndex, E value) { if (lowIndex < 0) { lowIndex = 0; } if (highIndex > length) { highIndex = length; } if (highIndex <= lowIndex) { // this check guarantees that overflow is impossible below: // offset + lowIndex <= offset + highIndex <= offset + length = toIndex <= Long.MAX_VALUE return -1; } long result = parent.indexOf(offset + lowIndex, offset + highIndex, value); return result == -1 ? -1 : result - offset; } @Override public long lastIndexOf(long lowIndex, long highIndex, E value) { if (lowIndex < 0) { lowIndex = 0; } if (highIndex > length) { highIndex = length; } if (highIndex <= lowIndex) { // this check guarantees that overflow is impossible below: // offset + lowIndex <= offset + highIndex <= offset + length = toIndex <= Long.MAX_VALUE return -1; } long result = parent.lastIndexOf(offset + lowIndex, offset + highIndex, value); return result == -1 ? -1 : result - offset; } @Override public void getData(long arrayPos, Object destArray, int destArrayOffset, int count) { if (count < 0) { throw new IllegalArgumentException("Negative number of loaded elements (" + count + ")"); } if (arrayPos < 0) { throw rangeException(arrayPos); } if (arrayPos > length - count) { throw rangeException(arrayPos + count - 1); } parent.getData(offset + arrayPos, destArray, destArrayOffset, count); } @Override public boolean isLazy() { return parent.isLazy(); } @Override protected void loadResources(ArrayContext context, long fromIndex, long toIndex) { parent.loadResources(context, offset + fromIndex, offset + toIndex); } @Override protected void flushResources( ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting) { parent.flushResources(context, offset + fromIndex, offset + toIndex, forcePhysicalWriting); } @Override protected void freeResources( ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting) { parent.freeResources(context, offset + fromIndex, offset + toIndex, forcePhysicalWriting); } }; } @Override public DataObjectBuffer buffer(DataBuffer.AccessMode mode, long capacity) { return InternalUtils.cast(super.buffer(mode, capacity)); } @Override public DataObjectBuffer buffer(DataBuffer.AccessMode mode) { return InternalUtils.cast(super.buffer(mode)); } @Override public DataObjectBuffer buffer(long capacity) { return InternalUtils.cast(super.buffer(capacity)); } @Override public DataObjectBuffer buffer() { return InternalUtils.cast(super.buffer()); } public abstract E get(long index); /** * This implementation is based on a loop of calls of {@link #get(long)} method * from index max(lowIndex,0) until index min({@link #length()},highIndex)-1. * Please override this method if it's possible to perform the same task more efficiently * than such a loop. * * @param lowIndex the low index in the array for search (inclusive). * @param highIndex the high index in the array for search (exclusive). * @param value the value to be found. * @return the index of the first occurrence of this value in this array * in range lowIndex<=index<highIndex, * or -1 if this value does not occur in this range. */ public long indexOf(long lowIndex, long highIndex, E value) { long k = Math.max(lowIndex, 0); long n = Math.min(length(), highIndex); if (value == null) { for (; k < n; k++) { if (get(k) == null) { return k; } } } else { for (; k < n; k++) { if (value.equals(get(k))) { return k; } } } return -1; } /** * This implementation is based on a loop of calls of {@link #get(long)} method * from index min({@link #length()},highIndex)-1 back until index max(lowIndex,0). * Please override this method if it's possible to perform the same task more efficiently * than such a loop. * * @param lowIndex the low index in the array for search (inclusive). * @param highIndex the high index in the array for search (exclusive). * @param value the value to be found. * @return the index of the last occurrence of this value in this array * in range lowIndex<=index<highIndex, * or -1 if this value does not occur in this range. */ public long lastIndexOf(long lowIndex, long highIndex, E value) { long k = Math.min(length(), highIndex); // warning: highIndex-1 can be invalid value Long.MAX_VALUE long low = Math.max(lowIndex, 0); if (value == null) { for (; k > low; ) { if (get(--k) == null) { return k; } } } else { for (; k > low; ) { if (value.equals(get(--k))) { return k; } } } return -1; } /** * This implementation returns true. * Should be overridden if the inheritor is mutable. * * @return true if this instance is immutable. */ @Override public boolean isImmutable() { return true; } /** * This implementation returns true. * Should be overridden if the inheritor is resizable * * @return true if this instance is unresizable. */ @Override public boolean isUnresizable() { return true; } /** * This implementation does nothing. * * @throws UnallowedMutationError never in this implementation. */ @Override public void checkUnallowedMutation() throws UnallowedMutationError { } /** * This implementation calls {@link #asImmutable()} and returns its result. * * @return a trusted immutable view of this array (or a reference to this array if it is already * trusted immutable). */ @Override public ObjectArray asTrustedImmutable() { return asImmutable(); } /** * This implementation returns this object. * Should be overridden if the inheritor is mutable. * * @return a copy-on-next-write view of this array (or a reference to this array if it is * immutable or already copy-on-next-write). */ @Override public Array asCopyOnNextWrite() { return this; } /** * This implementation returns false. * Should be overridden if the inheritor is mutable. * * @return true if this array is in copy-on-next-write mode */ @Override public boolean isCopyOnNextWrite() { return false; } /** * This implementation returns this object. * Should be overridden if the inheritor is mutable. * * @return an immutable view of this array (or a reference to this array if it is immutable). */ @Override public ObjectArray asImmutable() { return this; } @Override public MutableObjectArray mutableClone(MemoryModel memoryModel) { return InternalUtils.cast(super.mutableClone(memoryModel)); } @Override public UpdatableObjectArray updatableClone(MemoryModel memoryModel) { return InternalUtils.cast(super.updatableClone(memoryModel)); } @SuppressWarnings("unchecked") public E[] ja() { return (E[]) super.ja(); } /** * This implementation calls * {@link #loadResources(ArrayContext, long, long) loadResources(context, 0, length())}. * * @param context the context of execution; can be {@code null}, then it will be ignored. */ @Override public void loadResources(ArrayContext context) { loadResources(context, 0, length()); } /** * This implementation calls * {@link #flushResources(ArrayContext, long, long, boolean) * flushResources(context, 0, length(), forcePhysicalWriting)}. * * @param context the context of execution; can be {@code null}, then it will be ignored. * @param forcePhysicalWriting is it necessary to try forcing physical writing all associated resources * to the external device. */ @Override public void flushResources(ArrayContext context, boolean forcePhysicalWriting) { flushResources(context, 0, length(), forcePhysicalWriting); } /** * This implementation calls * {@link #freeResources(ArrayContext, long, long, boolean) * freeResources(context, 0, length()), forcePhysicalWriting)}. * * @param context the context of execution; can be {@code null}, then it will be ignored. * @param forcePhysicalWriting is it necessary to try forcing physical writing all associated resources * to the external device. */ @Override public void freeResources(ArrayContext context, boolean forcePhysicalWriting) { freeResources(context, 0, length(), forcePhysicalWriting); } /** * This method implements all actions that should be performed by * {@link #subArray subArray}(fromIndex, toIndex).{@link #loadResources(ArrayContext) * loadResources}(context) call. * This default implementation calls {@link #loadResources(ArrayContext) loadResources(c)} * (where c is a necessary {@link ArrayContext#part(long, long, long) part} of the passed context) * for the corresponding subarray * of all underlying arrays, passed via the last argument of the constructor, * if the underlyingArraysAreParallel constructor argument was true, * or does nothing in other case. * * @param context the context of execution; can be {@code null}, then it will be ignored. * @param fromIndex low endpoint (inclusive) of the subarray that should be loaded. * @param toIndex high endpoint (exclusive) of the subarray that should be loaded. * @throws IndexOutOfBoundsException for illegal fromIndex and toIndex * (fromIndex < 0 || toIndex > length() || fromIndex > toIndex). */ protected void loadResources(ArrayContext context, long fromIndex, long toIndex) { checkSubArrayArguments(fromIndex, toIndex); if (underlyingArraysAreParallel) { for (int k = 0; k < underlyingArrays.length; k++) { underlyingArrays[k].subArray(fromIndex, toIndex).loadResources( context == null ? null : context.part(k, k + 1, underlyingArrays.length)); } } } /** * This method implements all actions that should be performed by * {@link #subArray subArray}(fromIndex, toIndex).{@link #flushResources(ArrayContext, boolean) * flushResources(context, forcePhysicalWriting)} call. * This default implementation calls {@link #flushResources(ArrayContext, boolean) * flushResources(c, forcePhysicalWriting)} * (where c is a necessary {@link ArrayContext#part(long, long, long) part} of the passed context) * for the corresponding subarray of all underlying arrays, passed via the last argument of the constructor, * if the underlyingArraysAreParallel constructor argument was true, * or for original underlying arrays in other case * (alike {@link AbstractArray#flushResources(ArrayContext, boolean)}). * * @param context the context of execution; can be {@code null}, then it will be ignored. * @param fromIndex low endpoint (inclusive) of the subarray that should be flushed. * @param toIndex high endpoint (exclusive) of the subarray that should be flushed. * @param forcePhysicalWriting is it necessary to try forcing physical writing all associated resources * to the external device. * @throws IndexOutOfBoundsException for illegal fromIndex and toIndex * (fromIndex < 0 || toIndex > length() || fromIndex > toIndex). */ protected void flushResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting) { checkSubArrayArguments(fromIndex, toIndex); if (underlyingArraysAreParallel) { for (int k = 0; k < underlyingArrays.length; k++) { underlyingArrays[k].subArray(fromIndex, toIndex).flushResources( context == null ? null : context.part(k, k + 1, underlyingArrays.length), forcePhysicalWriting); } } else { super.flushResources(context, forcePhysicalWriting); } } /** * This method implements all actions that should be performed by * {@link #subArray subArray}(fromIndex, toIndex).{@link #freeResources(ArrayContext, boolean) * freeResources(context, forcePhysicalWriting)} call. * This default implementation calls {@link #freeResources(ArrayContext, boolean) * freeResources(c, forcePhysicalWriting)} * (where c is a necessary {@link ArrayContext#part(long, long, long) part} of the passed context) * for the corresponding subarray of all underlying arrays, passed via the last argument of the constructor, * if the underlyingArraysAreParallel constructor argument was true, * or for original underlying arrays in other case * (alike {@link AbstractArray#freeResources(ArrayContext, boolean)}). * * @param context the context of execution; can be {@code null}, then it will be ignored. * @param fromIndex low endpoint (inclusive) of the subarray that should be freed. * @param toIndex high endpoint (exclusive) of the subarray that should be freed. * @param forcePhysicalWriting is it necessary to try forcing physical writing all associated resources * to the external device. * @throws IndexOutOfBoundsException for illegal fromIndex and toIndex * (fromIndex < 0 || toIndex > length() || fromIndex > toIndex). */ protected void freeResources(ArrayContext context, long fromIndex, long toIndex, boolean forcePhysicalWriting) { checkSubArrayArguments(fromIndex, toIndex); if (underlyingArraysAreParallel) { for (int k = 0; k < underlyingArrays.length; k++) { underlyingArrays[k].subArray(fromIndex, toIndex).freeResources( context == null ? null : context.part(k, k + 1, underlyingArrays.length), forcePhysicalWriting); } } else { super.freeResources(context, forcePhysicalWriting); } } @Override public String toString() { return "immutable AlgART array " + elementType.getName() + "[" + length + "]" + (underlyingArrays.length == 0 ? "" : " based on " + underlyingArrays.length + " underlying array" + (underlyingArrays.length > 1 ? "s" : "")); } Object javaArrayInternal() { return null; } int javaArrayOffsetInternal() { return 0; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy