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

net.openhft.chronicle.map.AbstractChronicleMap Maven / Gradle / Ivy

/*
 * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com
 *
 * 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
 *
 * 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 net.openhft.chronicle.map;

import net.openhft.chronicle.hash.function.SerializableFunction;
import net.openhft.chronicle.hash.impl.hashlookup.HashLookupIteration;
import net.openhft.chronicle.hash.impl.util.CharSequences;
import net.openhft.chronicle.map.impl.CompiledMapIterationContext;
import net.openhft.chronicle.map.impl.IterationContextInterface;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;

import static java.util.Collections.emptyList;
import static net.openhft.chronicle.hash.impl.util.Objects.requireNonNull;

interface AbstractChronicleMap extends ChronicleMap, Serializable {

    @Override
    default  R getMapped(K key, @NotNull SerializableFunction function) {
        requireNonNull(function);
        try (MapKeyContext c = context(key)) {
            return c.containsKey() ? function.apply(c.get()) : null;
        }
    }

    @Override
    default V putMapped(K key, @NotNull UnaryOperator unaryOperator) {
        requireNonNull(unaryOperator);
        try (MapKeyContext c = context(key)) {
            // putMapped() should find a value, update & put most of the time,
            // so don't try to check key presence under read lock first,
            // as in putIfAbsent()/acquireUsing(), start with update lock:
            c.updateLock().lock();
            if (c.containsKey()) {
                V newValue = unaryOperator.update(c.get());
                c.put(newValue);
                return newValue;
            } else {
                return null;
            }
        }
    }

    @Override
    default void getAll(File toFile) throws IOException {
        synchronized (this) {
            JsonSerializer.getAll(toFile, this, emptyList());
        }
    }

    @Override
    default void putAll(File fromFile) throws IOException {
        synchronized (this) {
            JsonSerializer.putAll(fromFile, this, emptyList());
        }
    }

    int actualSegments();

    @Override
    default boolean containsValue(Object value) {
        return !forEachEntryWhile(c -> !c.valueEqualTo((V) value));
    }

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

    @Override
    default void forEach(BiConsumer action) {
        forEachEntry(c -> action.accept(c.key(), c.get()));
    }

    @Override
    default void putAll(Map m) {
        m.forEach(this::put);
    }

    @NotNull
    @Override
    default Collection values() {
        // todo cache view object
        return new AbstractCollection() {
            @Override
            public Iterator iterator() {
                return new Iterator() {
                    private Iterator> i = entrySet().iterator();

                    @Override
                    public boolean hasNext() {
                        return i.hasNext();
                    }

                    @Override
                    public V next() {
                        return i.next().getValue();
                    }

                    @Override
                    public void remove() {
                        i.remove();
                    }
                };
            }

            @Override
            public int size() {
                return AbstractChronicleMap.this.size();
            }

            @Override
            public boolean isEmpty() {
                return AbstractChronicleMap.this.isEmpty();
            }

            @Override
            public void clear() {
                AbstractChronicleMap.this.clear();
            }

            @Override
            public boolean contains(Object v) {
                return AbstractChronicleMap.this.containsValue(v);
            }

            @Override
            public void forEach(java.util.function.Consumer action) {
                AbstractChronicleMap.this.forEachEntry(c -> action.accept(c.get()));
            }
        };
    }

    @NotNull
    @Override
    default Set keySet() {
        // todo cache view object
        return new AbstractSet() {
            @Override
            public Iterator iterator() {
                return new ChronicleMapIterator.OfKeys<>(AbstractChronicleMap.this);
            }

            @Override
            public int size() {
                return AbstractChronicleMap.this.size();
            }

            public boolean isEmpty() {
                return AbstractChronicleMap.this.isEmpty();
            }

            public void clear() {
                AbstractChronicleMap.this.clear();
            }

            public boolean contains(Object k) {
                return AbstractChronicleMap.this.containsKey(k);
            }

            @Override
            public void forEach(java.util.function.Consumer action) {
                AbstractChronicleMap.this.forEachEntry(c -> action.accept(c.key()));
            }
        };
    }

    default boolean mapEquals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map m = (Map) o;
        if ((m instanceof ChronicleMap ? ((ChronicleMap) m).longSize() : m.size()) != longSize())
            return false;

        try {
            return forEachEntryWhile(c -> {
                K k = c.key();
                V v = (V) m.get(k instanceof CharSequence ? k.toString() : k);
                return v != null && c.valueEqualTo(v);
            });
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

    default int mapHashCode() {
        int[] h = new int[1];
        forEach((k, v) -> h[0] += hashCode(k) ^ hashCode(v));
        return h[0];
    }

    // TODO quick and dirty. Think about how generic guava/koloboke equivalence interface could be
    // used. See also BytesInterop.equivalent() and hash().
    public static int hashCode(Object obj) {
        if (!(obj instanceof CharSequence)) {
            return obj.hashCode();
        } else {
            return CharSequences.hash((CharSequence) obj);
        }
    }

    default String mapToString() {
        if (isEmpty())
            return "{}";
        StringBuilder sb = new StringBuilder();
        sb.append('{');
        forEach((k, v) -> sb
                .append(k != AbstractChronicleMap.this ? k : "(this Map)")
                .append('=')
                .append(v != AbstractChronicleMap.this ? v : "(this Map)")
                .append(',').append(' '));
        if (sb.length() > 2)
            sb.setLength(sb.length() - 2);
        sb.append('}');
        return sb.toString();
    }

    default Set> newEntrySet() {
        return new ChronicleMapEntrySet<>(this);
    }


    @Override
    default void forEachEntry(final Consumer> action) {
        forEachEntryWhile(c -> {
            action.accept(c);
            return true;
        });
    }
    
    IterationContextInterface iterationContext();

    @Override
    default boolean forEachEntryWhile(final Predicate> action) {
        boolean interrupt = false;
        iteration:
        try (IterationContextInterface c = iterationContext()) {
            for (int segmentIndex = actualSegments() - 1; segmentIndex >= 0; segmentIndex--) {
                c.initTheSegmentIndex(segmentIndex);
                if (!c.forEachRemoving(e -> action.test(c.deprecatedMapKeyContextOnIteration()))) {
                    interrupt = true;
                    break iteration;
                }
            }
        }
        return !interrupt;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy