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

functionalj.list.ImmutableFuncList Maven / Gradle / Ivy

There is a newer version: 1.0.17
Show newest version
// ============================================================================
// Copyright (c) 2017-2021 Nawapunth Manusitthipol (NawaMan - http://nawaman.net).
// ----------------------------------------------------------------------------
// MIT License
//
// 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 functionalj.list;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import functionalj.function.ObjectObjectToIntegerFunction;
import functionalj.result.Result;
import functionalj.stream.StreamPlus;
import functionalj.stream.markers.Sequential;
import functionalj.stream.markers.Terminal;
import lombok.val;


// TODO - Override methods in FuncListWithMapGroup to make it faster
// TODO - Override methods in FuncListWithMapWithIndex to make it faster


public final class ImmutableFuncList implements FuncList {
    
    private static final ObjectObjectToIntegerFunction zeroForEquals = (Object i1, Object i2) -> Objects.equals(i1, i2) ? 0 : 1;
    private static final Predicate                            toZero        = (Integer i)            -> i  == 0;
    
    private final static ImmutableFuncList EMPTY = new ImmutableFuncList<>(Collections.emptyList(), 0);
    
    /** @return an empty list */
    @SuppressWarnings("unchecked")
    public static final  ImmutableFuncList empty() {
        return (ImmutableFuncList)EMPTY;
    }
    
    /** @return the list containing the given elements */
    @SafeVarargs
    public static  ImmutableFuncList of(T ... data) {
        val list = new ArrayList(data.length);
        for (val each : data)
            list.add(each);
        return new ImmutableFuncList<>(list, list.size());
    }
    
    /** @return the list containing the given elements */
    @SafeVarargs
    public static  ImmutableFuncList listOf(T ... data) {
        val list = new ArrayList(data.length);
        for (val each : data)
            list.add(each);
        return new ImmutableFuncList(list, list.size());
    }
    
    /** Create a FuncList from the given array. */
    public static  ImmutableFuncList from(TARGET[] datas) {
        return from(Mode.lazy, datas);
    }
    
    /** Create a FuncList from the given array. */
    public static  ImmutableFuncList from(Mode mode, TARGET[] datas) {
        return ImmutableFuncList.of(datas);
    }
    
    /** @return the list containing the given elements */
    public static  FuncList from(Mode mode, AsFuncList funcList) {
        if (funcList == null)
            return ImmutableFuncList.empty();
        
        FuncList list = funcList.asFuncList();
        return new ImmutableFuncList(list, list.size(), mode);
    }
    
    /** @return the list containing the element from the given stream */
    public static  ImmutableFuncList from(Stream stream) {
        val list = stream.collect(Collectors.toList());
        return new ImmutableFuncList(list, list.size());
    }
    
    /** @return the list containing the element from the given stream */
    static  FuncList from(Mode mode, Stream stream) {
        val list = stream.collect(Collectors.toList());
        return new ImmutableFuncList(list, list.size(), mode);
    }
    
    /** @return the list containing the element from the given list. */
    public static  ImmutableFuncList from(ReadOnlyList readOnlyList) {
        if (readOnlyList instanceof ImmutableFuncList)
            return (ImmutableFuncList)readOnlyList;
        if (readOnlyList == null)
            return ImmutableFuncList.empty();
        
        val list = readOnlyList.toJavaList();
        return new ImmutableFuncList(list, list.size());
    }
    
    /** @return the list containing the element from the given collections. */
    public static  ImmutableFuncList from(Collection collection) {
        if (collection instanceof ImmutableFuncList)
            return (ImmutableFuncList)collection;
        if (collection == null)
            return ImmutableFuncList.empty();
        if (collection instanceof FuncList) {
            val funcList = (FuncList)collection;
            return new ImmutableFuncList(funcList.toJavaList(), funcList.size(), funcList.mode());
        }
        if (collection instanceof List) {
            val list = (List)collection;
            return new ImmutableFuncList(list, list.size(), Mode.lazy);
        }
        
        val list = (List)collection.stream().collect(Collectors.toList());
        return new ImmutableFuncList(list, list.size(), Mode.lazy);
    }
    
    //-- Data --
    
    private final List    data;
    private final FuncList.Mode mode;
    private final int           size;
    
    private volatile String  toStringCache = null;
    private volatile Integer hashcodeCache = null;
    
    //-- Constructors --
    
    ImmutableFuncList(Collection data, int size) {
        this(data, size, Mode.lazy);
    }
    
    ImmutableFuncList(Collection data, int size, Mode mode) {
        if (data == null) {
            this.data = Collections.emptyList();
        } else if (data instanceof ImmutableFuncList) {
            this.data = ((ImmutableFuncList)data).data;
        } else {
            val list = new ArrayList();
            data.forEach(list::add);
            this.data = list;
        }
        this.size = (size != -1) ? size : this.data.size();
        this.mode = mode;
    }
    
    @Override
    public StreamPlus stream() {
        if (size ==-1) {
            return StreamPlus.from(data.stream());
        } else {
            return StreamPlus.from(data.stream().limit(size));
        }
    }
    
    @Override
    public Mode mode() {
        return mode;
    }
    
    @Override
    public FuncList toLazy() {
        if (mode().isLazy())
            return this;
        
        // Do this to not duplicate the data
        return new ImmutableFuncList(data, size, Mode.lazy);
    }
    
    @Override
    public FuncList toEager() {
        if (mode().isEager())
            return this;
        
        // Do this to not duplicate the data
        return new ImmutableFuncList(data, size, Mode.eager);
    }
    
    @Override
    public FuncList toCache() {
        if (mode().isCache())
            return this;
        
        // Do this to not duplicate the data
        return new ImmutableFuncList(data, size, Mode.cache);
    }
    
    @Override
    public ImmutableFuncList toImmutableList() {
        return this;
    }
    
    @Override
    public String toString() {
        if (toStringCache != null)
            return toStringCache;
        
        synchronized (this) {
            if (toStringCache != null)
                return toStringCache;
            
            toStringCache = StreamPlus.from(data.stream().limit(size)).toListString();
            return toStringCache;
        }
    }
    
    @Override
    public int hashCode() {
        if (hashcodeCache != null)
            return hashcodeCache;
        
        synchronized (this) {
            if (hashcodeCache != null)
                return hashcodeCache;
            
            hashcodeCache
                    = mapToInt(value -> (value != null) ? Objects.hash(value) : 0)
                    .limit(size)
                    .reduce(43, (hash, each) -> hash*43 + each);
            return hashcodeCache;
        }
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof FuncList))
            return false;
        
        if (hashCode() != o.hashCode())
            return false;
        
        val anotherList = (FuncList)o;
        if (size() != anotherList.size())
            return false;
        
        return FuncList.zipOf(this, anotherList, (BiFunction)zeroForEquals)
                .allMatch(toZero);
    }
    
    // -- Short cut --
    
    @Override
    public int size() {
        return size;
    }
    
    @Override
    public boolean isEmpty() {
        return (size == 0);
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public  TARGET[] toArray(TARGET[] seed) {
        if (seed.length != size) {
            seed = (TARGET[])Array.newInstance(seed.getClass().getComponentType(), size);
        }
        
        val seedArray  = seed;
        val streamPlus = streamPlus();
        streamPlus
        .limit           (Math.min(size, seed.length - 0))
        .forEachWithIndex((index, element) -> {
            seedArray[index] = (TARGET)element;
        });
        return seed;
    }
    
    @Override
    public DATA get(int index) {
        if ((index < 0) || (index >= size)) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
        return data.get(index);
    }
    
    @Override
    public int indexOf(Object o) {
        int index = data.indexOf(o);
        if (index >= size) {
            return -1;
        }
        return index;
    }
    
    @Override
    public int lastIndexOf(Object o) {
        // TODO - Improve this efficiency
        return data.subList(0, size).lastIndexOf(o);
    }
    
    @Override
    public ListIterator listIterator() {
        // TODO - Improve this efficiency
        return data.subList(0, size).listIterator();
    }
    
    @Override
    public ListIterator listIterator(int index) {
        // TODO - Improve this efficiency
        return data.subList(0, size).listIterator();
    }
    
    @Sequential
    @Terminal
    @Override
    public Result firstResult() {
        return (size == 0)
                ? Result.ofNotExist()
                : Result.ofValue(this.data.get(0));
    }
    
    @Sequential
    @Terminal
    @Override
    public Result lastResult() {
        return (size == 0)
                ? Result.ofNotExist()
                : Result.ofValue(this.data.get(size - 1));
    }
    
    //-- Append
    
    /**
     * Add the given value to the end of the list.
     * This method is for convenient. It is not really efficient if used to add a lot of data.
     **/
    public FuncList append(DATA value) {
        if (this == EMPTY) {
            List list = new ArrayList();
            list.add(value);
            return new ImmutableFuncList(list, 1, mode());
        }
        
        return syncIf(
                () ->(data instanceof ArrayList) && (size == data.size()), 
                ()-> {
                    data.add(value);
                    return new ImmutableFuncList<>(data, data.size(), mode());
                },
                () -> {
                    return FuncList.super.append(value);
                });
    }
    
    /** Add the given values to the end of the list. */
    @SuppressWarnings("unchecked")
    public FuncList appendAll(DATA ... values) {
//        if (this == EMPTY) {
//            val list = new ArrayList(values.length);
//            for (DATA value : values) {
//                list.add(value);
//            }
//            return new ImmutableFuncList(list, list.size(), isLazy);
//        }
        
        return syncIf(
                () ->(data instanceof ArrayList) && (size == data.size()), 
                ()-> {
                    for (DATA value : values) {
                        data.add(value);
                    }
                    return new ImmutableFuncList<>(data, data.size(), mode());
                },
                () -> {
                    return FuncList.super.appendAll(values);
                });
    }
    
    /** Add the given value in the collection to the end of the list. */
    public FuncList appendAll(Collection collection) {
//        if (this == EMPTY) {
//            val list = new ArrayList(collection.size());
//            list.addAll(collection);
//            return new ImmutableFuncList(list, list.size(), isLazy);
//        }
        
        return syncIf(
                () ->(data instanceof ArrayList) && (size == data.size()), 
                ()-> {
                    data.addAll(collection);
                    return new ImmutableFuncList<>(data, data.size(), mode());
                },
                () -> {
                    return FuncList.super.appendAll(collection);
                });
    }
    
    private FuncList syncIf(
            BooleanSupplier           condition,
            Supplier>  matchAction,
            Supplier>  elseAction) {
        synchronized (this) {
            if (condition.getAsBoolean()) {
                return matchAction.get();
            }
        }
        return elseAction.get();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy