/*
 * Napisane przez Douga Leę z pomocą członków grupy ekspertów JCP JSR-166 i 
 * udostępnione publicznie, jak wyjaśniono w
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package extra166y;
import java.lang.ref.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import sun.misc.Unsafe;

/**
 * Klasa {@Link java.util.ConcurrentMap} obsługująca zdefiniowane
 * przez użytkownika porównania równoważności, miękkie i słabe lub
 * mocne klucze i wartości oraz utworzone przez użytkownika metody
 * obliczeniowe do ustawiania i aktualizowania wartości, w szczególności: <ul>
 *
 *   <li> Oparte na tożsamości, oparte na równości lub adefiniowane przez użytkownika
 *        oparte na {@link Equivalence} porównania kontrolujące przynależność.
 *
 *   <li> {@linkplain SoftReference słabe}, {@linkplain
 *        WeakReference miękkie} lub silne (zwykłe) klucze i wartości.
 *
 *   <li> Zdefiniowane przez użytkownika funkcje {@code MappingFunctions},
 *        które mogą być wykorzystywane w metodzie {@link
 *        CustomConcurrentHashMap#computeIfAbsent} do automatycznego
 *        ustanawiania wyliczonej wartości, wraz z funkcjami
 *        {@code RemappingFunctions}, które mogą wyć wykorzystywane w metodzie
 *        {@link CustomConcurrentHashMap#compute} do atomicznego
 *        zastępowania wartości.
 *
 *    <li>Metody fabryczne, zwracające specjalne formy {@code int}
 *        kluczy i wartości, które mogą zajmować mniej pamięci
 *
 * </ul>
 *
 * Ustawienia dla poszczególnych map są ustanawiane w konstruktorach,
 * podobnie jak w następujących zastosowaniach (które zakładają
 * importowanie statyczne w celu uproszczenia wyrażania parametrów
 * konfiguracyjnych):
 *
 * <pre>
 * {@code
 * identityMap = new CustomConcurrentHashMap<Person,Salary>
 *     (STRONG, IDENTITY, STRONG, EQUALS, 0);
 * weakKeyMap = new CustomConcurrentHashMap<Person,Salary>
 *     (WEAK, IDENTITY, STRONG, EQUALS, 0);
 *     .weakKeys());
 * byNameMap = new CustomConcurrentHashMap<Person,Salary>
 *     (STRONG,
 *      new Equivalence<Person>() {
 *          public boolean equal(Person k, Object x) {
 *            return x instanceof Person && k.name.equals(((Person)x).name);
 *          }
 *          public int hash(Object x) {
 *             return (x instanceof Person) ? ((Person)x).name.hashCode() : 0;
 *          }
 *        },
 *      STRONG, EQUALS, 0);
 * }
 * </pre>
 *
 * Pierwsze użycie powyżej zapewnia zastąpienie {@link java.util.IdentityHashMap},
 * a drugie zastąpienie {@link java.util.WeakHashMap}, dodanie współbieżności,
 * czyszczenia asynchronicznego i równości kluczy. Trzecie użycie ilustruje mapę
 * z niestandardowym równoważnikiem, który patrzy tylko na pole nazwy (fikcyjnej)
 * klasy Person.
 *
 * Ta klasa obejmuje także zagnieżdżoną klasę {@link KeySet}, która 
 * zapewnia przestrzenne widoki zestawów map, a także obsługuje 
 * metodę {@code intern}, która może być przydatna w elementach 
 * kanonizujących.
 *
 * <p>Jeżeli zostanie użyta z kluczami (słabymi lub miękkimi) i/lub wartościami
 * elementy, które asynchronicznie stały się {@code null} są traktowane jako 
 * nieobecne na mapie i (ewentualnie) usuwane z map za pomocą wątku w tłe
 * wspólnego dla wszystkich map. Ze względu na możliwość asynchronicznego
 * czyszczenia odwołań metody takie jak {@code containsValue} dają słabszą 
 * gwarancję, niż można się spodziewać, nawet przy braku innych jawnych operacji. 
 * Na przykład {@code containsValue(wartość)} może zwrócić wartość true, 
 * nawet jeśli {@code wartość} nie jest już dostępny po powrocie z metody.
 *
 * <p>Gdy używane są równoważności inne niż równość, zwrócone kolekcje mogą 
 * naruszać specyfikacje interfejsów {@code Map} i/obszar {@code Set}, które 
 * nakazują stosowanie metody {@code equals} podczas porównywania obiektów. 
 * Metody tej klasy w przeciwnym razie mają właściwości podobne do właściwości 
 * {@link java.util.ConcurrentHashMap} w ustawieniach domyślnych. Aby adaptacyjnie 
 * utrzymać semantykę i wydajność w różnych warunkach, ta klasa <em>nie</em> 
 * obsługuje współczynnika obciążenia ani parametrów poziomu współbieżności. 
 * Ta klasa nie zezwala na klucze lub wartości zerowe. Ta klasa jest serializowalna, 
 * jednak serializacja mapy z miękkimi lub słabymi odniesieniami może dawać 
 * nieprzewidywalne wyniki. Ta klasa obsługuje wszystkie opcjonalne operacje 
 * interfejsu {@code ConcurrentMap}. Obsługuje <i>słabo spójną iterację</i>: 
 * iterator nad jedną z kolekcji widoków mapy może odzwierciedlać niektóre, 
 * wszystkie lub żadne zmiany wprowadzone w kolekcji po utworzeniu iteratora.
 
 * <p>Ta klasa jest częścią
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <K> rodzaj kluczy obsługiwanych przez tę mapę
 * @param <V> rodzaj mapowanych wartości
 */
public class CustomConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {
    private static final long serialVersionUID = 7249069246764182397L;

    /*
     * Ta klasa stosuje podobne podejście jak ConcurrentHashMap, ale
     * dokonuje różnych wewnętrznych kompromisów, głównie: (1) używamy
     * więcej segmentów, ale leniwie je inicjujemy; oraz (2) łącza
     * łączące węzły nie są niezmienne, co umożliwia rozłączanie. Te
     * dwie korekty poprawiają współbieżność cięższej mechaniki
     * poszczególnych elementów i zwiększonego obciążenia spowodowanego
     * usunięciem odniesienia, przy jednoczesnym zachowaniu rozsądnego
     * obciążenia itp.     
     *
     * Dodatkowo, ponieważ klucze/wartości referencyjne mogą stać się
     * zerowe asynchronicznie, nie możemy zapewnić integralności
     * migawki w metodach takich jak containsValue, więc nie próbuj ich
     * uzyskiwać (więc nie używaj modCounts itp.).
     *
     * Ponadto zamieniona jest zmienność liczby segmentów i pól 
     * tabeli, co jest możliwe dzięki zapewnieniu ogrodzeń dla nowych
     * przypisań węzłów.
     */


    /**
     * Siła kluczy i wartości, które mogą znajdować się w mapach. strong
     * oznacza zwykłe obiekty, weak i soft oznaczają odpowiednie typy
     * {@link java.lang.ref.Reference}.
     */
    public enum Strength {
        strong("Strong"), weak("Weak"), soft("Soft");
        private final String name;
        Strength(String name) { this.name = name; }
        String getName() { return name; }
    };


    /** Siła zwykłych referencji * /
    public static final Strength STRONG = Strength.strong;

    / ** Siła słabych referencji * /
    public static final Strength WEAK   = Strength.weak;

    / ** Siła miękkich odniesień * /
    public static final Strength SOFT   = Strength.soft;

    /** Ciąg konfiguracyjny dla mapy Self (zestaw widoków) */
    private static final String SELF_STRING = "Self";

    /** Ciąg konfiguracyjny dla mapy Int */
    private static final String INT_STRING = "Int";

    /**
     * Obiekt sprawdzający równość wraz z funkcją skrótu zgodną z tym 
     * porównaniem. Sygnatury typów metod tego interfejsu
     * odzwierciedlają sygnatury {@link java.util.Map}: Mimo że tylko
     * elementy {@code K} mogą być wprowadzone do mapy, każdy {@code 
     * Object} może zostać przetestowany pod kątem członkostwa. Należy 
     * pamiętać, że wydajność map mieszających zależy w dużej mierze od 
     * jakości funkcji skrótu.
     */
    public static interface Equivalence<K> {
        /**
         * Zwraca true, jeśli dane obiekty zostaną uznane za równe. 
         * Metoda ta musi być zgodna z relacją równoważności: equal(a, 
         * a) jest zawsze true, equal(a, b) oznacza equal(b, a), a 
         * (equal(a, b) &amp;&amp; equal(b, c) oznacza equal(a, c). 
         * Zauważ, że drugi argument nie musi być tego samego typu
         * co pierwszy.
         
         * @param key klucz do umieszczenia na mapie
         * @param x obiekt, którego członkostwo jest sprawdzane
         * @return true, jeżeli obiekty są równe
         */
        boolean equal(K key, Object x);
        /**
         * Zwraca wartość mieszającą oznaczającą, że equal(a, b)
         * oznacza hash(a)==hash(b).
         * @param x obiekt, którego członkostwo jest sprawdzane
         * @return wartość mieszająca
         */
        int hash(Object x);
    }

    // Wbudowane równoważniki.

    static final class EquivalenceUsingIdentity
        implements Equivalence<Object>, Serializable {
        private static final long serialVersionUID = 7259069246764182397L;
        public final boolean equal(Object a, Object b) { return a == b; }
        public final int hash(Object a) { return System.identityHashCode(a); }
    }

    static final class EquivalenceUsingEquals
        implements Equivalence<Object>, Serializable {
        private static final long serialVersionUID = 7259069247764182397L;
        public final boolean equal(Object a, Object b) { return a.equals(b); }
        public final int hash(Object a) { return a.hashCode(); }
    }

    /**
     * Obiekt Equivalence wykonujący porównania oparte na tożsamości
     * i używający {@link System#identityHashCode} do mieszania.
     */
    public static final Equivalence<Object> IDENTITY =
        new EquivalenceUsingIdentity();

    /**
     * Obiekt Equivalence wykonujący porównania oparte na {@link 
     * Object#equals} i używający {@link Object#hashCode} do mieszania.
     */
    public static final Equivalence<Object> EQUALS =
        new EquivalenceUsingEquals();

    /**
     * Funkcja mapująca podany klucz na wartość lub {@code null}, 
     * jeśli nie ma mapowania.
     */
    public static interface MappingFunction<K,V> {
        /**
         * Zwraca wartość dla danego klucza lub null, jeśli nie ma 
         * mapowania. Jeśli ta funkcja zgłasza (niesprawdzony) wyjątek, 
         * wyjątek jest zwracany do jego obiektu wywołującego i żadne 
         * mapowanie nie jest rejestrowane. Ponieważ ta funkcja jest 
         * wywoływana w ramach kontroli atomowości, obliczenia powinny 
         * być krótkie i proste. Najczęstszym zastosowaniem jest 
         * budowanie nowego obiektu służącego jako początkowa wartość 
         * odwzorowana.
         * @param key klucz inny niż null
         * @return wartość lub null, jeżeli jej brak
         */
        V map(K key);
    }

    /**
     * Funkcja obliczająca nowe mapowanie z podanego klucza i jego 
     * bieżącej wartości na nową wartość lub {@code null}, jeśli nie ma 
     * mapowania.
     */
    public static interface RemappingFunction<K,V> {
        /**
         * Zwraca nową wartość dla danego klucza i jego bieżącego lub 
         * null, jeśli nie ma mapowania.
         * @param key klucz
         * @param value bieżąca wartość lub null, jeżeli jej brak
         * @return wartość lub null, jeżeli jej brak
         */
        V remap(K key, V value);
    }

    /**
     * Obiekt, który może podlegać operacjom czyszczenia po usunięciu z 
     * {@link java.lang.ref.ReferenceQueue}.
     */
    static interface Reclaimable {
        /**
         * Działanie podjęte po usunięciu tego obiektu 
         * z ReferenceQueue.
         */
        void onReclamation();
    }

    /**
     * Fabryka węzłów.
     */
    static interface NodeFactory extends Serializable {
        /**
         * Tworzy i zwraca Node przy użyciu podanych parametrów.
         *
         * @param locator nieprzezroczysty niezmienny lokalizator dla tego węzła
         * @param key (niezerowy) niezmienny klucz
         * @param value (nie-zerowa) wartość lotna
         * @param cchm tabela tworząca ten węzeł
         * @param linkage nieprzezroczyste ulotne połączenie dla utrzymania tego węzła
         */
        Node newNode(int locator, Object key, Object value,
                     CustomConcurrentHashMap cchm, Node linkage);
    }

    /**
     * Obiekt utrzymujący odwzorowanie klucz-wartość. Węzły 
     * udostępniają metody, które mają być używane <em>tylko</em> przez 
     * mapę tworzącą węzeł. Obejmuje to metody stosowane wyłącznie do 
     * wewnętrznego rejestrowania przez mapy, które muszą być 
     * traktowane nieprzezroczyście przez klasy implementacyjne. (Wymóg 
     * ten wynika z faktu, że do wykonania podklasy {@link 
     * java.lang.ref.Reference} lub innych klas może być konieczne 
     * zastosowanie konkretnych implementacji), dlatego nie można 
     * ustanowić klasy podstawowej.)
     *
     * Ten interfejs wykorzystuje surowe typy jako mniejsze zło. W 
     * przeciwnym razie napotkalibyśmy prawie tyle samo niesprawdzonych 
     * rzutów, gdy węzły są używane w zestawach itp.
     */
    static interface Node extends Reclaimable {
        /**
         * Zwraca klucz ustalony podczas tworzenia tego węzła. Uwaga: 
         * Ta metoda nosi nazwę "get", a nie "getKey", aby uprościć 
         * korzystanie z kluczy Reference.
         * @return klucz
         */
        Object get();

        /**
         * Zwraca lokalizator ustalony podczas tworzenia tego węzła.
         * @return lokalizator
         */
        int getLocator();

        /**
         * Zwraca wartość ustaloną podczas tworzenia tego węzła lub, 
         * jeśli po aktualizacji, wartość ustawioną przez ostatnie 
         * wywołanie setValue, lub zgłasza wyjątek, jeśli nie można 
         * było obliczyć wartości.
         * @return wartość
         * @throws RuntimeException lub Error jeżeli computeValue nie powiedzie się
         */
        Object getValue();

        /**
         * Wartość, która ma zostać zwrócona przez następne wywołanie getValue.
         * @param value wartość
         */
        void setValue(Object value);

        /**
         * Zwraca powiązanie utworzone podczas tworzenia tego węzła 
         * lub, jeśli uaktualnione, połączenie ustawione przez ostatnie 
         * wywołanie setLinkage.
         * @return powiązanie
         */
        Node getLinkage();

        /**
         * Rejestruje powiązanie, które zostanie zwrócone przez następne wywołanie getLinkage.
         * @param linkage powiązanie
         */
        void setLinkage(Node linkage);
    }

    /**
     * Każdy segment zawiera liczbę i tabelę odpowiadającą segmentowi 
     * tabeli. Ta klasa zawiera tylko te metody bezpośredniego 
     * przypisujące pola, które można wywoływać tylko przy trzymaniu 
     * blokad.
     */
    static final class Segment extends ReentrantLock {
        volatile Node[] table;
        int count;

        final void decrementCount() {
            if (--count == 0)
                table = null;
        }

        final void clearCount() {
            count = 0;
            table = null;
        }

        final void incrementCount() {
            ++count;
        }

        final Node[] getTableForTraversal() {
            return table;
        }

        final Node[] getTableForAdd(CustomConcurrentHashMap cchm) {
            int len;
            Node[] tab = table;
            if (tab == null ||  // 3/4 threshold
                ((len = tab.length) - (len >>> 2)) < count)
                return resizeTable(cchm);
            else
                return tab;
        }

        /**
         * Zobacz podobny kod w ConcurrentHashMap dla wyjaśnienia.
         */
        final Node[] resizeTable(CustomConcurrentHashMap cchm) {
            Node[] oldTable = table;
            if (oldTable == null)
                return table = new Node[cchm.initialSegmentCapacity];

            int oldCapacity = oldTable.length;
            if (oldCapacity >= MAX_SEGMENT_CAPACITY)
                return oldTable;
            Node[] newTable = new Node[oldCapacity<<1];
            int sizeMask = newTable.length - 1;
            NodeFactory fac = cchm.factory;
            for (int i = 0; i < oldCapacity ; i++) {
                Node e = oldTable[i];

                if (e != null) {
                    Node next = e.getLinkage();
                    int idx = e.getLocator() & sizeMask;

                    //  Pojedynczy węzeł na liście
                    if (next == null)
                        newTable[idx] = e;

                    else {
                        // Ponownie użyj końcowej sekwencji w tym samym slocie
                        Node lastRun = e;
                        int lastIdx = idx;
                        for (Node last = next;
                             last != null;
                             last = last.getLinkage()) {
                            int k = last.getLocator() & sizeMask;
                            if (k != lastIdx) {
                                lastIdx = k;
                                lastRun = last;
                            }
                        }
                        newTable[lastIdx] = lastRun;

                        // Sklonuj wszystkie pozostałe węzły
                        for (Node p = e; p != lastRun; p = p.getLinkage()) {
                            int ph = p.getLocator();
                            int k = ph & sizeMask;
                            Object pk = p.get();
                            Object pv;
                            if (pk == null ||
                                (pv = p.getValue()) == null)
                                --count;
                            else
                                newTable[k] =
                                    fac.newNode(ph, pk, pv, cchm, newTable[k]);
                        }
                    }
                }
            }
            return table = newTable;
        }
    }

    // Sztywne połaczenie 64 segmentów

    static final int SEGMENT_BITS         = 6;
    static final int NSEGMENTS            = 1 << SEGMENT_BITS;
    static final int SEGMENT_MASK         = NSEGMENTS - 1;
    static final int SEGMENT_SHIFT        = 32 - SEGMENT_BITS;
    static final int MIN_SEGMENT_CAPACITY = 4;
    static final int MAX_SEGMENT_CAPACITY = 1 << (32 - SEGMENT_BITS);

    /**
     * Stosuje dodatkową funkcję skrótu do danego kodu skrótu, który 
     * chroni przed funkcjami skrótu niskiej jakości. Jest to 
     * krytyczne, ponieważ używamy tablic mieszających o długościach
     * równych potędze dwóch. Bez tego pojawiałyby się kolizje dla 
     * kodów mieszających, które nie różnią się bitami dolnymi ani 
     * górnymi.
     */
    static int spreadHash(int h) {
        // Rozłóż bity, aby wyregulować zarówno lokalizację segmentu, 
        // jak i indeks, używając wariantu skrótu jednosłowowego
        // Wang / Jenkins.
        h += (h <<  15) ^ 0xffffcd7d;
        h ^= (h >>> 10);
        h += (h <<   3);
        h ^= (h >>>  6);
        h += (h <<   2) + (h << 14);
        return h ^ (h >>> 16);
    }

    /**
     * Segmenty, z których każdy działa jak tablica skrótów
     */
    transient volatile Segment[] segments;

    /**
     * Fabryka tej mapy
     */
    final NodeFactory factory;

    /**
     * Obiekt równoważności dla kluczy
     */
    final Equivalence<? super K> keyEquivalence;

    /**
     * Obiekt Equivalence object dla wartości
     */
    final Equivalence<? super V> valueEquivalence;

    /**
     * Początkowy rozmiar tabel segmentów podczas ich pierwszej budowy
     */
    final int initialSegmentCapacity;

    // Cached view objects
    transient Set<K> keySet;
    transient Set<Map.Entry<K,V>> entrySet;
    transient Collection<V> values;

    /**
     * Wewnętrzny konstruktor do ustawiania fabryki, ekwiwalentów i 
     * pojemności segmentów oraz do tworzenia tablicy segmentów.
     */
    CustomConcurrentHashMap(String ks, Equivalence<? super K> keq,
                            String vs, Equivalence<? super V> veq,
                            int expectedSize) {
        if (keq == null || veq == null)
            throw new NullPointerException();
        this.keyEquivalence = keq;
        this.valueEquivalence = veq;
        String factoryName =
            CustomConcurrentHashMap.class.getName() + "$" +
            ks + "Key" +
            vs + "ValueNodeFactory";
        try {
            this.factory = (NodeFactory)
                (Class.forName(factoryName).newInstance());
        } catch (Exception ex) {
            throw new Error("Nie można utworzyć instancji " + factoryName);
        }
        int es = expectedSize;
        if (es == 0)
            this.initialSegmentCapacity = MIN_SEGMENT_CAPACITY;
        else {
            int sc = (int)((1L + (4L * es) / 3) >>> SEGMENT_BITS);
            if (sc < MIN_SEGMENT_CAPACITY)
                sc = MIN_SEGMENT_CAPACITY;
            int capacity = MIN_SEGMENT_CAPACITY; // Potęga dwóch
            while (capacity < sc)
                capacity <<= 1;
            if (capacity > MAX_SEGMENT_CAPACITY)
                capacity = MAX_SEGMENT_CAPACITY;
            this.initialSegmentCapacity = capacity;
        }
        this.segments = new Segment[NSEGMENTS];
    }

    /**
     * Tworzy nową CustomConcurrentHashMap z podanymi parametrami.
     * @param keyStrength siła kluczy
     * @param keyEquivalence obiekt Equivalence wykorzystywany z kluczami
     * @param valueStrength siła wartości
     * @param valueEquivalence obiekt Equivalence wykorzystywany z wartościami
     * @param expectedSize szacunkowa liczba elementów, które będą 
     * przechowywane na mapie. Jeśli oszacowanie nie jest znane, zero 
     * jest wartością dopuszczalną.
     */
    public CustomConcurrentHashMap(Strength keyStrength,
                                   Equivalence<? super K> keyEquivalence,
                                   Strength valueStrength,
                                   Equivalence<? super V> valueEquivalence,
                                   int expectedSize) {
        this(keyStrength.getName(), keyEquivalence,
             valueStrength.getName(), valueEquivalence,
             expectedSize);
    }

    /**
     * Tworzy nową CustomConcurrentHashMap z silnymi kluczami i 
     * wartościami oraz równoważnością opartą na równości.
     */
    public CustomConcurrentHashMap() {
        this(STRONG, EQUALS, STRONG, EQUALS, 0);
    }

    /**
     * Zwraca nową mapę za pomocą kluczy całkowitych i podanych 
     * parametrów wartości.
     * @param valueStrength siła wartości
     * @param valueEquivalence obiekt Equivalence wykorzystywany z wartościami
     * @param expectedSize szacunkowa liczba elementów, które będą 
     * przechowywane na mapie. Jeśli oszacowanie nie jest znane, zero 
     * jest wartością dopuszczalną.
     * @return mapa
     */
    public static <ValueType> CustomConcurrentHashMap<Integer, ValueType>
        newIntKeyMap(Strength valueStrength,
                     Equivalence<? super ValueType> valueEquivalence,
                     int expectedSize) {
        return new CustomConcurrentHashMap<Integer, ValueType>
            (INT_STRING, EQUALS, valueStrength.getName(), valueEquivalence,
             expectedSize);
    }

    /**
     * Zwraca nową mapę przy użyciu podanych kluczowych parametrów i wartości całkowitych.
     * @param keyStrength siła kluczy
     * @param keyEquivalence obiekt Equivalence wykorzystywany z kluczami
     * @param expectedSize szacunkowa liczba elementów, które będą 
     * przechowywane na mapie. Jeśli oszacowanie nie jest znane, zero 
     * jest wartością dopuszczalną.
     * @return mapa
     */
    public static <KeyType> CustomConcurrentHashMap<KeyType, Integer>
        newIntValueMap(Strength keyStrength,
                       Equivalence<? super KeyType> keyEquivalence,
                       int expectedSize) {
        return new CustomConcurrentHashMap<KeyType, Integer>
            (keyStrength.getName(), keyEquivalence, INT_STRING, EQUALS,
             expectedSize);
    }

    /**
     * Zwraca nową mapę za pomocą kluczy i wartości całkowitych.
     * @param expectedSize szacunkowa liczba elementów, które będą 
     * przechowywane na mapie. Jeśli oszacowanie nie jest znane, zero 
     * jest wartością dopuszczalną.
     * @return mapa
     */
    public static CustomConcurrentHashMap<Integer, Integer>
        newIntKeyIntValueMap(int expectedSize) {
        return new CustomConcurrentHashMap<Integer, Integer>
            (INT_STRING, EQUALS, INT_STRING, EQUALS,
             expectedSize);
    }

    /**
     * Zwraca segment do przeglądania tabeli pod kątem klucza z danym hashem.
     * @param hash kod skrótu dla klucza
     * @return segment lub null, jeśli nie zostało jeszcze zainicjowane
     */
    final Segment getSegmentForTraversal(int hash) {
        return segments[(hash >>> SEGMENT_SHIFT) & SEGMENT_MASK];
    }

    /**
     * Zwraca segment do ewentualnego wstawienia do tabeli powiązanej
     * z danym kodem, konstruując go w razie potrzeby.
     * @param hash kod skrótu dla klucza
     * @return segment
     */
    final Segment getSegmentForAdd(int hash) {
        Segment[] segs = segments;
        int index = (hash >>> SEGMENT_SHIFT) & SEGMENT_MASK;
        Segment seg = segs[index];
        if (seg == null) {
            synchronized (segs) {
                seg = segs[index];
                if (seg == null) {
                    seg = new Segment();
                    // Fences.preStoreFence(seg);
                    // segs[index] = seg;
                    storeSegment(segs, index, seg);
                }
            }
        }
        return seg;
    }

    /**
     * Zwraca węzeł dla klucza lub null, jeśli nie go ma.
     */
    final Node findNode(Object key, int hash, Segment seg) {
        if (seg != null) {
            Node[] tab = seg.getTableForTraversal();
            if (tab != null) {
                Node p = tab[hash & (tab.length - 1)];
                while (p != null) {
                    Object k = p.get();
                    if (k == key ||
                        (k != null &&
                         p.getLocator() == hash &&
                         keyEquivalence.equal((K)k, key)))
                        return p;
                    p = p.getLinkage();
                }
            }
        }
        return null;
    }

    /**
     * Zwraca {@code true}, jeśli mapa zawiera klucz równoważny 
     * podanemu kluczowi w odniesieniu do równoważnika klucza tej mapy.
     *
     * @param  key możliwy klucz
     * @return {@code true} jeśli ta mapa zawiera podany klucz
     * @throws NullPointerException jeśli podany klucz ma wartość null
     */
    public boolean containsKey(Object key) {
        if (key == null)
            throw new NullPointerException();
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        Node r = findNode(key, hash, seg);
        return r != null && r.getValue() != null;
    }

    /**
     * Zwraca wartość powiązaną z kluczem równoważną podanemu kluczowi 
     * w odniesieniu do klucza Equivalence tej mapy lub {@code null}, 
     * jeśli takie mapowanie nie istnieje.
     *
     * @param  key możliwy klucz
     * @return wartość powiązana z kluczem lub {@code null}, jeśli nie ma mapowania
     * @throws NullPointerException jeśli określony klucz ma wartość null
     */
    public V get(Object key) {
        if (key == null)
            throw new NullPointerException();
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        Node r = findNode(key, hash, seg);
        if (r == null)
            return null;
        return (V)(r.getValue());
    }

    /**
     * Wspólna implementacja dla put, putIfAbsent
     */
    final V doPut(K key, V value, boolean onlyIfNull) {
        if (key == null || value == null)
            throw new NullPointerException();
        V oldValue = null;
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForAdd(hash);
        seg.lock();
        try {
            Node r = findNode(key, hash, seg);
            if (r != null) {
                oldValue = (V)(r.getValue());
                if (!onlyIfNull || oldValue == null)
                    r.setValue(value);
            }
            else {
                Node[] tab = seg.getTableForAdd(this);
                int i = hash & (tab.length - 1);
                r = factory.newNode(hash, key, value, this, tab[i]);
                // Fences.preStoreFence(r);
                // tab[i] = r;
                storeNode(tab, i, r);
                seg.incrementCount();
            }
        } finally {
            seg.unlock();
        }
        return oldValue;
    }

    /**
     * Mapuje określony klucz na określoną wartość na tej mapie.
     *
     * @param key klucz, z którym ma być powiązana podana wartość
     * @param value wartość do powiązania z podanym kluczem
     * @return poprzednia wartość powiązana z {@code key} lub {@code null}, jeśli nie było mapowania dla {@code key}
     * @throws NullPointerException jeśli podany klucz lub wartość ma wartość null
     */
    public V put(K key, V value) {
        return doPut(key, value, false);
    }

    /**
     * {@inheritDoc}
     *
     * @return poprzednia wartość powiązana z {@code key} lub {@code null}, jeśli nie było mapowania dla {@code key}
     * @throws NullPointerException jeśli podany klucz lub wartość ma wartość null
     */
    public V putIfAbsent(K key, V value) {
        return doPut(key, value, true);
    }

    /**
     * Kopiuje wszystkie mapowania z podanej mapy do niniejszej. Te 
     * mapowania zastępują wszelkie poprzednie, które ta mapa miała dla 
     * dowolnego klucza znajdującego się obecnie we wskazanej mapie.
     *
     * @param m odwzorowania, które mają być przechowywane w tej mapie
     */
    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException jeśli którykolwiek z argumentów ma wartość null
     */
    public V replace(K key, V value) {
        if (key == null || value == null)
            throw new NullPointerException();
        V oldValue = null;
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        if (seg != null) {
            seg.lock();
            try {
                Node r = findNode(key, hash, seg);
                if (r != null) {
                    oldValue = (V)(r.getValue());
                    r.setValue(value);
                }
            } finally {
                seg.unlock();
            }
        }
        return oldValue;
    }

    /**
     * {@inheritDoc}
     *
     * @return poprzednia wartość powiązana z podanym kluczem lub 
     * {@code null}, jeśli nie było mapowania dla klucza
     * @throws NullPointerException jeśli podany klucz lub wartość ma wartość null
     */
    public boolean replace(K key, V oldValue, V newValue) {
        if (key == null || oldValue == null || newValue == null)
            throw new NullPointerException();
        boolean replaced = false;
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        if (seg != null) {
            seg.lock();
            try {
                Node r = findNode(key, hash, seg);
                if (r != null) {
                    V v = (V)(r.getValue());
                    if (v == oldValue ||
                        (v != null && valueEquivalence.equal(v, oldValue))) {
                        r.setValue(newValue);
                        replaced = true;
                    }
                }
            } finally {
                seg.unlock();
            }
        }
        return replaced;
    }

    /**
     * Usuwa mapowanie dla podanego klucza.
     *
     * @param  key klucz do usunięcia
     * @return poprzednia wartość powiązana z podanym kluczem lub 
     * {@code null}, jeśli nie było mapowania dla klucza
     * @throws NullPointerException jeśli podany klucz lub wartość ma wartość null
     */
    public V remove(Object key) {
        if (key == null)
            throw new NullPointerException();
        V oldValue = null;
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        if (seg != null) {
            seg.lock();
            try {
                Node[] tab = seg.getTableForTraversal();
                if (tab != null) {
                    int i = hash & (tab.length - 1);
                    Node pred = null;
                    Node p = tab[i];
                    while (p != null) {
                        Node n = p.getLinkage();
                        Object k = p.get();
                        if (k == key ||
                            (k != null &&
                             p.getLocator() == hash &&
                             keyEquivalence.equal((K)k, key))) {
                            oldValue = (V)(p.getValue());
                            if (pred == null)
                                tab[i] = n;
                            else
                                pred.setLinkage(n);
                            seg.decrementCount();
                            break;
                        }
                        pred = p;
                        p = n;
                    }
                }
            } finally {
                seg.unlock();
            }
        }
        return oldValue;
    }

    /**
     * {@inheritDoc}
     *
     * @throws NullPointerException jeśli podany klucz ma wartość null
     */
    public boolean remove(Object key, Object value) {
        if (key == null)
            throw new NullPointerException();
        if (value == null)
            return false;
        boolean removed = false;
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        if (seg != null) {
            seg.lock();
            try {
                Node[] tab = seg.getTableForTraversal();
                if (tab != null) {
                    int i = hash & (tab.length - 1);
                    Node pred = null;
                    Node p = tab[i];
                    while (p != null) {
                        Node n = p.getLinkage();
                        Object k = p.get();
                        if (k == key ||
                            (k != null &&
                             p.getLocator() == hash &&
                             keyEquivalence.equal((K)k, key))) {
                            V v = (V)(p.getValue());
                            if (v == value ||
                                (v != null &&
                                 valueEquivalence.equal(v, value))) {
                                if (pred == null)
                                    tab[i] = n;
                                else
                                    pred.setLinkage(n);
                                seg.decrementCount();
                                removed = true;
                            }
                            break;
                        }
                        pred = p;
                        p = n;
                    }
                }
            } finally {
                seg.unlock();
            }
        }
        return removed;
    }

    /**
     * Usuwa węzeł, jeśli jego klucz lub wartość są puste.
     */
    final void removeIfReclaimed(Node r) {
        int hash = r.getLocator();
        Segment seg = getSegmentForTraversal(hash);
        if (seg != null) {
            seg.lock();
            try {
                Node[] tab = seg.getTableForTraversal();
                if (tab != null) {
                    // usuń wszystkie odzyskane z listy
                    int i = hash & (tab.length - 1);
                    Node pred = null;
                    Node p = tab[i];
                    while (p != null) {
                        Node n = p.getLinkage();
                        if (p.get() != null && p.getValue() != null) {
                            pred = p;
                            p = n;
                        }
                        else {
                            if (pred == null)
                                tab[i] = n;
                            else
                                pred.setLinkage(n);
                            seg.decrementCount();
                            p = n;
                        }
                    }
                }
            } finally {
                seg.unlock();
            }
        }
    }

    /**
     * Zwraca {@code true}, jeśli mapa nie zawiera mapowań klucz-wartość.
     *
     * @return {@code true} jeśli mapa zawiera mapowania klucz-wartość.
     */
    public final boolean isEmpty() {
        final Segment[] segs = this.segments;
        for (int i = 0; i < segs.length; ++i) {
            Segment seg = segs[i];
            if (seg != null &&
                seg.getTableForTraversal() != null &&
                seg.count != 0)
                return false;
        }
        return true;
    }

    /**
     * Zwraca liczbę mapowań klucz-wartość w tej mapie. Jeśli mapa 
     * zawiera więcej niż {@code Integer.MAX_VALUE} elementów, zwraca
     * {@code Integer.MAX_VALUE}.
     *
     * @return liczba mapowań klucz-wartość na tej mapie
     */
    public final int size() {
        long sum = 0;
        final Segment[] segs = this.segments;
        for (int i = 0; i < segs.length; ++i) {
            Segment seg = segs[i];
            if (seg != null && seg.getTableForTraversal() != null)
                sum += seg.count;
        }
        return (sum >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) sum;
    }

    /**
     * Zwraca {@code true}, jeśli mapa odwzorowuje jeden lub więcej 
     * kluczy na wartość równoważną podanej wartości w odniesieniu do  
     * wartości Equivalence tej mapy. Uwaga: ta metoda wymaga pełnego 
     * wewnętrznego przejścia tabeli skrótów, a zatem jest znacznie 
     * wolniejsza niż metoda {@code zawieraKlucz}.
     *
     * @param value wartość, której obecność na tej mapie ma zostać przetestowana
     * @return {@code true} jeśli ta mapa odwzorowuje jeden lub więcej kluczy na zadaną wartość
     * @throws NullPointerException jeśli podana wartość jest null
     */
    public final boolean containsValue(Object value) {
        if (value == null)
            throw new NullPointerException();
        Segment[] segs = this.segments;
        for (int i = 0; i < segs.length; ++i) {
            Segment seg = segs[i];
            Node[] tab;
            if (seg != null && (tab = seg.getTableForTraversal()) != null) {
                for (int j = 0; j < tab.length; ++j) {
                    for (Node p = tab[j];
                         p != null;
                         p = p.getLinkage()) {
                        V v = (V)(p.getValue());
                        if (v == value ||
                            (v != null && valueEquivalence.equal(v, value)))
                            return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Usuwa wszystkie mapowania z tej mapy.
     */
    public final void clear() {
        Segment[] segs = this.segments;
        for (int i = 0; i < segs.length; ++i) {
            Segment seg = segs[i];
            if (seg != null) {
                seg.lock();
                try {
                    seg.clearCount();
                } finally {
                    seg.unlock();
                }
            }
        }
    }

    /**
     * Jeśli podany klucz nie jest już powiązany z wartością, oblicza 
     * jego wartość za pomocą podanej funkcji mappingFunction, a jeśli 
     * nie ma wartości null, wprowadza ją do mapy. Jest to równoważne z:
     *
     * <pre>
     *   if (map.containsKey(key))
     *       return map.get(key);
     *   value = mappingFunction.map(key);
     *   if (value != null)
     *      return map.put(key, value);
     *   else
     *      return null;
     * </pre>
     *
     * z wyjątkiem tego, że akcja jest wykonywana atomicznie. Niektóre 
     * próby operacji na tej mapie przez inne wątki mogą być blokowane 
     * podczas obliczeń. Ponieważ ta funkcja jest wywoływana w ramach 
     * kontroli atomiczności, obliczenia powinny być krótkie i proste. 
     * Najczęstszym zastosowaniem jest konstruowanie nowego obiektu 
     * służącego jako początkowa mapowana wartość lub zapamiętywany wynik.
     *
     * @param key klucz, z którym ma być powiązana zadana wartość
     * @param mappingFunction funkcja wyiczająca wartość
     * @return bieżąca (istniejąca lub obliczona) wartość powiązana z 
     * zadanym  kluczem lub {@code null}, jeśli obliczenia zwróciły 
     * {@code null}
     * @throws RuntimeException lub Error jeśli funkcja 
     * mappingFunction tak robi, w takim przypadku mapowanie nie 
     * zostanie ustanowione
     */
    public V computeIfAbsent(K key, MappingFunction<? super K, ? extends V> mappingFunction) {
        if (key == null || mappingFunction == null)
            throw new NullPointerException();
        int hash = spreadHash(keyEquivalence.hash(key));
        Segment seg = getSegmentForTraversal(hash);
        Node r = findNode(key, hash, seg);
        V v = null;
        if (r == null) {
            if (seg == null)
                seg = getSegmentForAdd(hash);
            seg.lock();
            try {
                r = findNode(key, hash, seg);
                if (r == null || (v = (V)(r.getValue())) == null) {
                    // Mapa jest OK, jeśli funkcja zgłasza wyjątek
                    v = mappingFunction.map(key);
                    if (v != null) {
                        if (r != null)
                            r.setValue(v);
                        else {
                            Node[] tab = seg.getTableForAdd(this);
                            int i = hash & (tab.length - 1);
                            r = factory.newNode(hash, key, v, this, tab[i]);
                            // Fences.preStoreFence(r);
                            // tab[i] = r;
                            storeNode(tab, i, r);
                            seg.incrementCount();
                        }
                    }
                }
            } finally {
                seg.unlock();
            }
        }
        if (r != null && v == null)
            removeIfReclaimed(r);
        return v;
    }

    /**
     * Aktualizuje mapowanie dla danego klucza z wynikiem danej 
     * funkcji remappingFunction. Jest to równoważne z
     *
     * <pre>
     *   value = remappingFunction.remap(key, get(key));
     *   if (value != null)
     *     return put(key, value):
     *   else
     *     return remove(key);
     * </pre>
     *
     * z wyjątkiem tego, że akcja jest wykonywana atomicznie. Niektóre 
     * próby operacji na tej mapie przez inne wątki mogą być blokowane 
     * podczas obliczeń.
     *
     * <p>Przykładowe użycie. Funkcja ponownego mapowania może być 
     * użyta do zliczania częstotliwości słów za pomocą kodu, np. 
     * <pre>
     * map.compute(word, new RemappingFunction&lt;String,Integer&gt;() {
     *   public Integer remap(String k, Integer v) {
     *     return (v == null) ? 1 : v + 1;
     *   }});
     * </pre>
     *
     * @param key klucz, z którym ma być powiązana określona wartość
     * @param remappingFunction funkcja wyliczająca wartość
     * @return zaktualizowana wartość lub {@code null}, jeśli 
     * obliczenia zwróciły {@code null}
     * @throws NullPointerException jeśli zadany klucz lub funkcja 
     * remappingFunction ma wartość null
     * @throws RuntimeException lub Error jeśli funkcja 
     * remappingFunction mapowania tak robi, w takim przypadku 
     * mapowanie pozostaje w poprzednim stanie
     */
    public V compute(K key, RemappingFunction<? super K, V> remappingFunction) {
        if (key == null || remappingFunction == null)
            throw new NullPointerException();
        int hash = spreadHash(keyEquivalence.hash(key));
        V value = null;
        Segment seg = getSegmentForAdd(hash);
        seg.lock();
        try {
            Node[] tab = seg.getTableForAdd(this);
            int i = hash & (tab.length - 1);
            Node pred = null;
            Node p = tab[i];
            while (p != null) {
                K k = (K)(p.get());
                if (k == key ||
                    (k != null &&
                     p.getLocator() == hash &&
                     keyEquivalence.equal(k, key))) {
                    value = (V)(p.getValue());
                    break;
                }
                pred = p;
                p = p.getLinkage();
            }
            value = remappingFunction.remap(key, value);
            if (p != null) {
                if (value != null)
                    p.setValue(value);
                else {
                    Node n = p.getLinkage();
                    if (pred == null)
                        tab[i] = n;
                    else
                        pred.setLinkage(n);
                    seg.decrementCount();
                }
            }
            else if (value != null) {
                Node r =
                    factory.newNode(hash, key, value, this, tab[i]);
                // Fences.preStoreFence(r);
                // tab[i] = r;
                storeNode(tab, i, r);
                seg.incrementCount();
            }
        } finally {
            seg.unlock();
        }
        return value;
    }

    abstract class HashIterator {
        int nextSegmentIndex;
        int nextTableIndex;
        Node[] currentTable;
        Node nextNode;
        Object nextKey;           // klucz węzła nextNode
        Object nextValue;         // wartość węzła nextNode
        Object lastKey;           // dla remove()

        HashIterator() {
            nextSegmentIndex = segments.length - 1;
            nextTableIndex = -1;
            advance();
        }

        public final boolean hasNext() { return nextNode != null; }

        final void advance() {
            lastKey = nextKey;
            if (nextNode != null)
                nextNode = nextNode.getLinkage();
            for (;;) {
                if (nextNode != null) {
                    if ((nextKey = nextNode.get()) != null &&
                        (nextValue = nextNode.getValue()) != null)
                        return;
                    Node n = nextNode.getLinkage();
                    removeIfReclaimed(nextNode);
                    nextNode = n;
                }
                else if (nextTableIndex >= 0) {
                    nextNode = currentTable[nextTableIndex--];
                }
                else if (nextSegmentIndex >= 0) {
                    Segment seg = segments[nextSegmentIndex--];
                    Node[] t;
                    if (seg != null &&
                        (t = seg.getTableForTraversal()) != null) {
                        currentTable = t;
                        nextTableIndex = t.length - 1;
                    }
                }
                else {
                    nextKey = null;
                    nextValue = null;
                    return;
                }
            }
        }

        final K nextKey() {
            if (nextNode == null)
                throw new NoSuchElementException();
            Object k = nextKey;
            advance();
            return (K)k;
        }

        final V nextValue() {
            if (nextNode == null)
                throw new NoSuchElementException();
            Object v = nextValue;
            advance();
            return (V)v;
        }

        final Map.Entry<K,V> nextEntry() {
            if (nextNode == null)
                throw new NoSuchElementException();
            WriteThroughEntry e = new WriteThroughEntry((K)nextKey,
                                                        (V)nextValue);
            advance();
            return e;
        }

        public void remove() {
            if (lastKey == null)
                throw new IllegalStateException();
            CustomConcurrentHashMap.this.remove(lastKey);
            lastKey = null;
        }
    }

    final class WriteThroughEntry implements Map.Entry<K,V>, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        final K key;
        V value;
        WriteThroughEntry(K key, V value) {
            this.key = key;
            this.value = value;
        }
        public K getKey() { return key; }
        public V getValue() { return value; }
        public V setValue(V value) {
            if (value == null) throw new NullPointerException();
            V v = this.value;
            this.value = value;
            CustomConcurrentHashMap.this.doPut(key, value, false);
            return v;
        }
        public int hashCode() {
            return keyEquivalence.hash(key) ^ valueEquivalence.hash(value);
        }
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            return (keyEquivalence.equal(key, e.getKey()) &&
                    valueEquivalence.equal(value, e.getValue()));
        }
    }

    final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public K next() {
            return super.nextKey();
        }
    }

    final KeyIterator keyIterator() { // Wymagane przez Set
        return new KeyIterator();
    }

    final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public V next() {
            return super.nextValue();
        }
    }

    final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public Map.Entry<K,V> next() {
            return super.nextEntry();
        }
    }

    final class KeySetView extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return new KeyIterator();
        }
        public int size() {
            return CustomConcurrentHashMap.this.size();
        }
        public boolean isEmpty() {
            return CustomConcurrentHashMap.this.isEmpty();
        }
        public boolean contains(Object o) {
            return CustomConcurrentHashMap.this.containsKey(o);
        }
        public boolean remove(Object o) {
            return CustomConcurrentHashMap.this.remove(o) != null;
        }
        public void clear() {
            CustomConcurrentHashMap.this.clear();
        }
    }

    final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return new ValueIterator();
        }
        public int size() {
            return CustomConcurrentHashMap.this.size();
        }
        public boolean isEmpty() {
            return CustomConcurrentHashMap.this.isEmpty();
        }
        public boolean contains(Object o) {
            return CustomConcurrentHashMap.this.containsValue(o);
        }
        public void clear() {
            CustomConcurrentHashMap.this.clear();
        }
    }

    final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            V v = CustomConcurrentHashMap.this.get(e.getKey());
            return v != null &&
                valueEquivalence.equal(v, e.getValue());
        }
        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            return CustomConcurrentHashMap.this.remove(e.getKey(),
                                                       e.getValue());
        }
        public int size() {
            return CustomConcurrentHashMap.this.size();
        }
        public boolean isEmpty() {
            return CustomConcurrentHashMap.this.isEmpty();
        }
        public void clear() {
            CustomConcurrentHashMap.this.clear();
        }
    }

    /**
     * Zwraca widok {@link Set} kluczy zawartych na tej mapie. Zestaw 
     * jest wspierany przez mapę, więc zmiany na mapie są 
     * odzwierciedlane w zestawie i odwrotnie. Zestaw obsługuje 
     * usuwanie elementów, które usuwają odpowiednie mapowanie z tej 
     ( mapy za pomocą operaacji {@code Iterator.remove}, {@code Set.remove}, 
     * {@code removeAll}, {@code retainAll} i {@code clear }. Nie 
     * obsługuje operacji {@code add} ani {@code addAll}.
     *
     * <p>{@code iterator} widoku jest iteratorem "słabo spójnym", 
     * który nigdy nie zgłosi {@link ConcurrentModificationException} 
     * i gwarantuje przechodzenie elementów, jakie istniały po 
     * zbudowaniu iteratora, i może (ale nie gwarantuje) 
     * odzwierciedlenie jakichkolwiek modyfikacji po budowie.
     */
    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null) ? ks : (keySet = new KeySetView());
    }

    /**
     * Zwraca widok {@link Collection} wartości zawartych w tej mapie. Kolekcja 
     * jest wspierana przez mapę, więc zmiany na mapie są odzwierciedlane w 
     * kolekcji i odwrotnie. Kolekcja obsługuje usuwanie elementów, które 
     * usuwają odpowiednie mapowanie z tej mapy za pośrednictwem operacji
     * {@code Iterator.remove},  {@code Collection.remove}, {@code removeAll}, 
     * {@code retainAll} i {@code clear}. Nie obsługuje operacji {@code add} 
     * ani {@code addAll}.
     *
     * <p>{@code iterator} widoku jest iteratorem "słabo spójnym", 
     * który nigdy nie zgłosi {@link ConcurrentModificationException} 
     * i gwarantuje przechodzenie elementów, jakie istniały po 
     * zbudowaniu iteratora, i może (ale nie gwarantuje) 
     * odzwierciedlenie jakichkolwiek modyfikacji po budowie.
     */
    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null) ? vs : (values = new Values());
    }

    /**
     * Zwraca widok {@link Set} mapowań zawartych na tej mapie. Zestaw jest 
     * wspierany przez mapę, więc zmiany na mapie są odzwierciedlane w zestawie 
     * i odwrotnie. Zestaw obsługuje usuwanie elementów, które usuwają 
     * odpowiednie mapowanie z mapy za pomocą operacji {@code Iterator.remove}, 
     * {@code Set.remove}, {@code removeAll}, {@code retainAll} i 
     * {@code clear } operacji. Nie obsługuje operacji {@code add} ani
     * {@code addAll}.
     *
     * <p>{@code iterator} widoku jest iteratorem "słabo spójnym", 
     * który nigdy nie zgłosi {@link ConcurrentModificationException} 
     * i gwarantuje przechodzenie elementów, jakie istniały po 
     * zbudowaniu iteratora, i może (ale nie gwarantuje) 
     * odzwierciedlenie jakichkolwiek modyfikacji po budowie.
     */
    public Set<Map.Entry<K,V>> entrySet() {
        Set<Map.Entry<K,V>> es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }

    // przesłonięte metody AbstractMap

    /**
     * Porównuje zadany obiekt z tą mapą pod kątem równości. Zwraca 
     * {@code true}, jeśli dany obiekt jest również mapą tego samego rozmiaru, 
     * zawierającą klucze, które są równe przy użyciu równoważnika klucza tej 
     * mapy, i które odwzorowują na wartości, które są równe zgodnie z 
     * równoważnością wartości tej mapy.
     * @param o obiekt do porównania dla równości z tą mapą
     * @return {@code true} jeśli zadany obiekt jest równy tej mapie
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<K,V> m = (Map<K,V>) o;
        if (m.size() != size())
            return false;

        try {
            Iterator<Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value != null) {
                    V mv = m.get(key);
                    if (mv == null)
                        return false;
                    if (!valueEquivalence.equal(mv, value))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

    /**
     * Zwraca sumę kodów skrótu każdego wpisu w widoku {@code entrySet ()} tej 
     * mapy, które z kolei są kodami skrótu obliczonymi przy użyciu klucza i 
     * wartości Equivalences dla tej mapy.
     * @return kod skrótu
     */
    public int hashCode() {
        int h = 0;
        Iterator<Entry<K,V>> i = entrySet().iterator();
        while (i.hasNext())
            h += i.next().hashCode();
        return h;
    }

    /**
     * Zapisuje stan instancji w strumieniu (tj. uerializuje go).
     *
     * @param s strumień
     * @serialData
     * klucz (Object) i wartość (Object)
     * dla każdego mapowania klucz-wartość, po którym następuje para zerowa. 
     * Odwzorowania klucz-wartość nie są emitowane w określonej kolejności.
     */
    private void writeObject(java.io.ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        for (Map.Entry<K,V> e : entrySet()) {
            s.writeObject(e.getKey());
            s.writeObject(e.getValue());
        }
        s.writeObject(null);
        s.writeObject(null);
    }

    /**
     * Odtwarza instancję ze strumienia (to znaczy deserializuje ją).
     * @param s strumień
     */
    private void readObject(java.io.ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        this.segments = new Segment[NSEGMENTS];
        for (;;) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            if (key == null)
                break;
            put(key, value);
        }
    }

    /**
     * Zestaw oparty na haszowaniu o właściwościach identycznych jak 
     * {@code Collections.newSetFromMap} zastosowany do 
     * {@code CustomConcurrentHashMap}, ale być może zajmuje więcej miejsca. 
     * Zestaw nie zezwala na elementy zerowe. Zestaw można serializować; jednak 
     * serializacja zestawu, który korzysta z miękkich lub słabych odniesień, 
     * może dać nieprzewidywalne wyniki.
     */
    public static class KeySet<K> extends AbstractSet<K>
        implements Set<K>, Serializable {

        final CustomConcurrentHashMap<K,K> cchm;

        /**
         * Tworzy zestaw z podanymi parametrami.
         * @param strength siła elementów
         * @param equivalence obiekt Equivalence
         * @param expectedSize szacunkowa liczba elementów, które będą 
         * przechowywane w zestawie. Jeśli oszacowanie nie jest znane, 
         * można użyć wartości 0.
         */
        public KeySet(Strength strength,
                      Equivalence<? super K> equivalence,
                      int expectedSize) {
            this.cchm = new CustomConcurrentHashMap<K,K>
                (strength.getName(), equivalence,
                 SELF_STRING, equivalence, expectedSize);
        }

        /**
         * Zwraca element równoważny podanemu elementowi w odniesieniu do 
         * równoważności tego zestawu, jeśli taki element istnieje, w 
         * przeciwnym razie dodaje i zwraca dany element.
         *
         * @param e element
         * @return e lub element równoważny e
         */
        public K intern(K e) {
            K oldElement = cchm.doPut(e, e, true);
            return (oldElement != null) ? oldElement : e;
        }

        /**
         * Zwraca {@code true}, jeśli ten zestaw zawiera element równoważny 
         * podanemu elementowi w odniesieniu do równoważności tego zestawu.
         * @param o element, którego obecność w tym zestawie ma zostać przetestowana
         * @return {@code true} jeśli ten zestaw zawiera określony element
         */
        public boolean contains(Object o) {
            return cchm.containsKey(o);
        }

        /**
         * Zwraca <i> słabo spójny iterator</i> nad elementami tego zestawu, 
         * które mogą odzwierciedlać niektóre, wszystkie lub żadne zmiany 
         * wprowadzone w zestawie po zresetowaniu iteratora.
         *
         * @return Iterator nad elementami w tym zestawie
         */
        public Iterator<K> iterator() {
            return cchm.keyIterator();
        }

        /**
         * Dodaje określony element do tego zestawu, jeśli nie istnieje już 
         * element równoważny podanemu elementowi w odniesieniu do 
         * równoważności tego zestawu.
         *
         * @param e element do dodania do tego zestawu
         * @return {@code true} jeśli ten zestaw nie zawierał już określonego elementu
         */
        public boolean add(K e) {
            return cchm.doPut(e, e, true) != null;
        }

        /**
         * Usuwa element równoważny podanemu elementowi w odniesieniu do 
         * równoważności tego zestawu, jeśli taki jest obecny.
         *
         * @param o obiekt do usunięcia z tego zestawu, jeśli jest obecny
         * @return {@code true} jeśli zestaw zawiera określony element
         */
        public boolean remove(Object o) {
            return cchm.remove(o) != null;
        }

        /**
         * Returns {@code true} jeśli ten zestaw nie zawiera elementów.
         *
         * @return {@code true} jeśli ten zestaw nie zawiera elementów.
         */
        public boolean isEmpty() {
            return cchm.isEmpty();
        }

        /**
         * Zwraca liczbę elementów w tym zestawie (jego liczność).
         *
         * @return liczba elementów w tym zestawie (jego liczność).
         */
        public int size() {
            return cchm.size();
        }

        /**
         * Usuwa wszystkie elementy z tego zestawu.
         */
        public void clear() {
            cchm.clear();
        }

        /**
         * Zwraca sumę kodów skrótu każdego elementu obliczoną na podstawie 
         * równoważności tego zestawu.
         * @return kod skrótu
         */
        public int hashCode() {
            int h = 0;
            Iterator<K> i = iterator();
            while (i.hasNext())
                h += cchm.keyEquivalence.hash(i.next());
            return h;
        }
    }

    // Reference queue mechanics for reclaimable nodes

    static volatile ReferenceQueue<Object> refQueue;

    /**
     * Zwraca kolejkę, która może zostać użyta jako argument ReferenceQueue do 
     * konstruktorów {@link java.lang.ref.Reference} w celu zorganizowania 
     * usunięcia odzyskanych węzłów z map za pomocą wątku w tle.
     * @return kolejka referencyjna powiązana z wątkiem czyszczącym w tłe
     */
    static ReferenceQueue<Object> getReclamationQueue() {
        ReferenceQueue<Object> q = refQueue;
        if (q != null)
            return q;
        else
            return startReclamation();
    }

    static synchronized ReferenceQueue<Object> startReclamation() {
            ReferenceQueue<Object> q = refQueue;
            if (q == null) {
                refQueue = q = new ReferenceQueue<Object>();
                new ReclamationThread(q).start();
            }
            return q;
        }

    static final class ReclamationThread extends Thread {
        final ReferenceQueue<Object> queue;
        ReclamationThread(ReferenceQueue<Object> q) {
            this.queue = q;
            setDaemon(true);
        }
        public void run() {
            ReferenceQueue<Object> q = queue;
            for (;;) {
                try {
                    Reference<?> r = q.remove();
                    if (r instanceof Reclaimable)
                        ((Reclaimable)r).onReclamation();
                } catch (InterruptedException e) {
                    /* ignore */
                }
            }
        }
    }

    // klasy rozszerzające słabe / miękkie referencje osadzone w węzłach możliwych do odzyskania

    static class EmbeddedWeakReference extends WeakReference
        implements Reclaimable {
        final Reclaimable outer;
        EmbeddedWeakReference(Object x, Reclaimable outer) {
            super(x, getReclamationQueue());
            this.outer = outer;
        }
        public final void onReclamation() {
            clear();
            outer.onReclamation();
        }
    }

    static class EmbeddedSoftReference extends SoftReference
        implements Reclaimable {
        final Reclaimable outer;
        EmbeddedSoftReference(Object x, Reclaimable outer) {
            super(x, getReclamationQueue());
            this.outer = outer;
        }
        public final void onReclamation() {
            clear();
            outer.onReclamation();
        }
    }

    // Wbudowane klasy węzłów odwzorowujących

    // Silne klucze

    abstract static class StrongKeyNode implements Node {
        final Object key;
        final int locator;
        StrongKeyNode(int locator, Object key) {
            this.locator = locator;
            this.key = key;
        }
        public final Object get() { return key; }
        public final int getLocator() { return locator; }
    }


    abstract static class StrongKeySelfValueNode
        extends StrongKeyNode {
        StrongKeySelfValueNode(int locator, Object key) {
            super(locator, key);
        }
        public final Object getValue() { return key; }
        public final void setValue(Object value) { }
        public final void onReclamation() { }
    }

    static final class TerminalStrongKeySelfValueNode
        extends StrongKeySelfValueNode {
        TerminalStrongKeySelfValueNode(int locator, Object key) {
            super(locator, key);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedStrongKeySelfValueNode
        extends StrongKeySelfValueNode {
        volatile Node linkage;
        LinkedStrongKeySelfValueNode(int locator, Object key,
                                     Node linkage) {
            super(locator, key);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class StrongKeySelfValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalStrongKeySelfValueNode
                    (locator, key);
            else
                return new LinkedStrongKeySelfValueNode
                    (locator, key, linkage);
        }
    }

    abstract static class StrongKeyStrongValueNode
        extends StrongKeyNode {
        volatile Object value;
        StrongKeyStrongValueNode(int locator, Object key, Object value) {
            super(locator, key);
            this.value = value;
        }
        public final Object getValue() { return value; }
        public final void setValue(Object value) { this.value = value; }
        public final void onReclamation() { }
    }

    static final class TerminalStrongKeyStrongValueNode
        extends StrongKeyStrongValueNode {
        TerminalStrongKeyStrongValueNode(int locator,
                                         Object key, Object value) {
            super(locator, key, value);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedStrongKeyStrongValueNode
        extends StrongKeyStrongValueNode {
        volatile Node linkage;
        LinkedStrongKeyStrongValueNode(int locator,
                                       Object key, Object value,
                                       Node linkage) {
            super(locator, key, value);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class StrongKeyStrongValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalStrongKeyStrongValueNode
                    (locator, key, value);
            else
                return new LinkedStrongKeyStrongValueNode
                    (locator, key, value, linkage);
        }
    }

    // ...

    abstract static class StrongKeyIntValueNode
        extends StrongKeyNode {
        volatile int value;
        StrongKeyIntValueNode(int locator, Object key, Object value) {
            super(locator, key);
            this.value = ((Integer)value).intValue();
        }
        public final Object getValue() { return Integer.valueOf(value); }
        public final void setValue(Object value) {
            this.value = ((Integer)value).intValue();
        }
        public final void onReclamation() { }
    }

    static final class TerminalStrongKeyIntValueNode
        extends StrongKeyIntValueNode {
        TerminalStrongKeyIntValueNode(int locator,
                                         Object key, Object value) {
            super(locator, key, value);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedStrongKeyIntValueNode
        extends StrongKeyIntValueNode {
        volatile Node linkage;
        LinkedStrongKeyIntValueNode(int locator,
                                       Object key, Object value,
                                       Node linkage) {
            super(locator, key, value);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class StrongKeyIntValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalStrongKeyIntValueNode
                    (locator, key, value);
            else
                return new LinkedStrongKeyIntValueNode
                    (locator, key, value, linkage);
        }
    }

    // ...

    abstract static class StrongKeyWeakValueNode
        extends StrongKeyNode {
        volatile EmbeddedWeakReference valueRef;
        final CustomConcurrentHashMap cchm;
        StrongKeyWeakValueNode(int locator, Object key, Object value,
                               CustomConcurrentHashMap cchm) {
            super(locator, key);
            this.cchm = cchm;
            if (value != null)
                this.valueRef = new EmbeddedWeakReference(value, this);
        }
        public final void onReclamation() {
            cchm.removeIfReclaimed(this);
        }
        public final Object getValue() {
            EmbeddedWeakReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedWeakReference(value, this);
        }
    }

    static final class TerminalStrongKeyWeakValueNode
        extends StrongKeyWeakValueNode {
        TerminalStrongKeyWeakValueNode(int locator,
                                       Object key, Object value,
                                       CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedStrongKeyWeakValueNode
        extends StrongKeyWeakValueNode {
        volatile Node linkage;
        LinkedStrongKeyWeakValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm,
                                     Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class StrongKeyWeakValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalStrongKeyWeakValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedStrongKeyWeakValueNode
                    (locator, key, value, cchm, linkage);
        }
    }


    abstract static class StrongKeySoftValueNode
        extends StrongKeyNode {
        volatile EmbeddedSoftReference valueRef;
        final CustomConcurrentHashMap cchm;
        StrongKeySoftValueNode(int locator, Object key, Object value,
                               CustomConcurrentHashMap cchm) {
            super(locator, key);
            this.cchm = cchm;
            if (value != null)
                this.valueRef = new EmbeddedSoftReference(value, this);
        }
        public final void onReclamation() {
            cchm.removeIfReclaimed(this);
        }
        public final Object getValue() {
            EmbeddedSoftReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedSoftReference(value, this);
        }
    }

    static final class TerminalStrongKeySoftValueNode
        extends StrongKeySoftValueNode {
        TerminalStrongKeySoftValueNode(int locator,
                                       Object key, Object value,
                                       CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedStrongKeySoftValueNode
        extends StrongKeySoftValueNode {
        volatile Node linkage;
        LinkedStrongKeySoftValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm,
                                     Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class StrongKeySoftValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalStrongKeySoftValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedStrongKeySoftValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    // Słabe klucze

    abstract static class WeakKeyNode extends WeakReference
        implements Node {
        final int locator;
        final CustomConcurrentHashMap cchm;
        WeakKeyNode(int locator, Object key, CustomConcurrentHashMap cchm) {
            super(key, getReclamationQueue());
            this.locator = locator;
            this.cchm = cchm;
        }
        public final int getLocator() { return locator; }
        public final void onReclamation() {
            clear();
            cchm.removeIfReclaimed(this);
        }
    }

    abstract static class WeakKeySelfValueNode
        extends WeakKeyNode {
        WeakKeySelfValueNode(int locator, Object key,
                             CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
        }
        public final Object getValue() { return get(); }
        public final void setValue(Object value) { }
    }

    static final class TerminalWeakKeySelfValueNode
        extends WeakKeySelfValueNode {
        TerminalWeakKeySelfValueNode(int locator, Object key,
                                     CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedWeakKeySelfValueNode
        extends WeakKeySelfValueNode {
        volatile Node linkage;
        LinkedWeakKeySelfValueNode(int locator, Object key,
                                   CustomConcurrentHashMap cchm,
                                   Node linkage) {
            super(locator, key, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class WeakKeySelfValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalWeakKeySelfValueNode
                    (locator, key, cchm);
            else
                return new LinkedWeakKeySelfValueNode
                    (locator, key, cchm, linkage);
        }
    }


    abstract static class WeakKeyStrongValueNode
        extends WeakKeyNode {
        volatile Object value;
        WeakKeyStrongValueNode(int locator, Object key, Object value,
                               CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            this.value = value;
        }
        public final Object getValue() { return value; }
        public final void setValue(Object value) { this.value = value; }
    }

    static final class TerminalWeakKeyStrongValueNode
        extends WeakKeyStrongValueNode {
        TerminalWeakKeyStrongValueNode(int locator,
                                       Object key, Object value,
                                       CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedWeakKeyStrongValueNode
        extends WeakKeyStrongValueNode {
        volatile Node linkage;
        LinkedWeakKeyStrongValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm,
                                     Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class WeakKeyStrongValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalWeakKeyStrongValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedWeakKeyStrongValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    abstract static class WeakKeyIntValueNode
        extends WeakKeyNode {
        volatile int value;
        WeakKeyIntValueNode(int locator, Object key, Object value,
                            CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            this.value = ((Integer)value).intValue();
        }
        public final Object getValue() { return Integer.valueOf(value); }
        public final void setValue(Object value) {
            this.value = ((Integer)value).intValue();
        }
    }

    static final class TerminalWeakKeyIntValueNode
        extends WeakKeyIntValueNode {
        TerminalWeakKeyIntValueNode(int locator,
                                    Object key, Object value,
                                    CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedWeakKeyIntValueNode
        extends WeakKeyIntValueNode {
        volatile Node linkage;
        LinkedWeakKeyIntValueNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class WeakKeyIntValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalWeakKeyIntValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedWeakKeyIntValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    abstract static class WeakKeyWeakValueNode
        extends WeakKeyNode {
        volatile EmbeddedWeakReference valueRef;
        WeakKeyWeakValueNode(int locator, Object key, Object value,
                             CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            if (value != null)
                this.valueRef = new EmbeddedWeakReference(value, this);
        }
        public final Object getValue() {
            EmbeddedWeakReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedWeakReference(value, this);
        }
    }

    static final class TerminalWeakKeyWeakValueNode
        extends WeakKeyWeakValueNode {
        TerminalWeakKeyWeakValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedWeakKeyWeakValueNode
        extends WeakKeyWeakValueNode {
        volatile Node linkage;
        LinkedWeakKeyWeakValueNode(int locator,
                                   Object key, Object value,
                                   CustomConcurrentHashMap cchm,
                                   Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class WeakKeyWeakValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalWeakKeyWeakValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedWeakKeyWeakValueNode
                    (locator, key, value, cchm, linkage);
        }
    }


    abstract static class WeakKeySoftValueNode
        extends WeakKeyNode {
        volatile EmbeddedSoftReference valueRef;
        WeakKeySoftValueNode(int locator, Object key, Object value,
                             CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            if (value != null)
                this.valueRef = new EmbeddedSoftReference(value, this);
        }
        public final Object getValue() {
            EmbeddedSoftReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedSoftReference(value, this);
        }
    }

    static final class TerminalWeakKeySoftValueNode
        extends WeakKeySoftValueNode {
        TerminalWeakKeySoftValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedWeakKeySoftValueNode
        extends WeakKeySoftValueNode {
        volatile Node linkage;
        LinkedWeakKeySoftValueNode(int locator,
                                   Object key, Object value,
                                   CustomConcurrentHashMap cchm,
                                   Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class WeakKeySoftValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalWeakKeySoftValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedWeakKeySoftValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    // Miękkie klucze

    abstract static class SoftKeyNode extends SoftReference
        implements Node {
        final int locator;
        final CustomConcurrentHashMap cchm;
        SoftKeyNode(int locator, Object key, CustomConcurrentHashMap cchm) {
            super(key, getReclamationQueue());
            this.locator = locator;
            this.cchm = cchm;
        }
        public final int getLocator() { return locator; }
        public final void onReclamation() {
            clear();
            cchm.removeIfReclaimed(this);
        }
    }

    abstract static class SoftKeySelfValueNode
        extends SoftKeyNode {
        SoftKeySelfValueNode(int locator, Object key,
                             CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
        }
        public final Object getValue() { return get(); }
        public final void setValue(Object value) { }
    }

    static final class TerminalSoftKeySelfValueNode
        extends SoftKeySelfValueNode {
        TerminalSoftKeySelfValueNode(int locator, Object key,
                                     CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedSoftKeySelfValueNode
        extends SoftKeySelfValueNode {
        volatile Node linkage;
        LinkedSoftKeySelfValueNode(int locator, Object key,
                                   CustomConcurrentHashMap cchm,
                                   Node linkage) {
            super(locator, key, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class SoftKeySelfValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalSoftKeySelfValueNode
                    (locator, key, cchm);
            else
                return new LinkedSoftKeySelfValueNode
                    (locator, key, cchm, linkage);
        }
    }


    abstract static class SoftKeyStrongValueNode
        extends SoftKeyNode {
        volatile Object value;
        SoftKeyStrongValueNode(int locator, Object key, Object value,
                               CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            this.value = value;
        }
        public final Object getValue() { return value; }
        public final void setValue(Object value) { this.value = value; }
    }

    static final class TerminalSoftKeyStrongValueNode
        extends SoftKeyStrongValueNode {
        TerminalSoftKeyStrongValueNode(int locator,
                                       Object key, Object value,
                                       CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedSoftKeyStrongValueNode
        extends SoftKeyStrongValueNode {
        volatile Node linkage;
        LinkedSoftKeyStrongValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm,
                                     Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class SoftKeyStrongValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalSoftKeyStrongValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedSoftKeyStrongValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    abstract static class SoftKeyIntValueNode
        extends SoftKeyNode {
        volatile int value;
        SoftKeyIntValueNode(int locator, Object key, Object value,
                            CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            this.value = ((Integer)value).intValue();
        }
        public final Object getValue() { return Integer.valueOf(value); }
        public final void setValue(Object value) {
            this.value = ((Integer)value).intValue();
        }
    }

    static final class TerminalSoftKeyIntValueNode
        extends SoftKeyIntValueNode {
        TerminalSoftKeyIntValueNode(int locator,
                                    Object key, Object value,
                            CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedSoftKeyIntValueNode
        extends SoftKeyIntValueNode {
        volatile Node linkage;
        LinkedSoftKeyIntValueNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class SoftKeyIntValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalSoftKeyIntValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedSoftKeyIntValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    abstract static class SoftKeyWeakValueNode
        extends SoftKeyNode {
        volatile EmbeddedWeakReference valueRef;
        SoftKeyWeakValueNode(int locator, Object key, Object value,
                             CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            if (value != null)
                this.valueRef = new EmbeddedWeakReference(value, this);
        }
        public final Object getValue() {
            EmbeddedWeakReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedWeakReference(value, this);
        }
    }

    static final class TerminalSoftKeyWeakValueNode
        extends SoftKeyWeakValueNode {
        TerminalSoftKeyWeakValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedSoftKeyWeakValueNode
        extends SoftKeyWeakValueNode {
        volatile Node linkage;
        LinkedSoftKeyWeakValueNode(int locator,
                                   Object key, Object value,
                                   CustomConcurrentHashMap cchm,
                                   Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class SoftKeyWeakValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalSoftKeyWeakValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedSoftKeyWeakValueNode
                    (locator, key, value, cchm, linkage);
        }
    }


    abstract static class SoftKeySoftValueNode
        extends SoftKeyNode {
        volatile EmbeddedSoftReference valueRef;
        SoftKeySoftValueNode(int locator, Object key, Object value,
                             CustomConcurrentHashMap cchm) {
            super(locator, key, cchm);
            if (value != null)
                this.valueRef = new EmbeddedSoftReference(value, this);
        }
        public final Object getValue() {
            EmbeddedSoftReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedSoftReference(value, this);
        }
    }

    static final class TerminalSoftKeySoftValueNode
        extends SoftKeySoftValueNode {
        TerminalSoftKeySoftValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedSoftKeySoftValueNode
        extends SoftKeySoftValueNode {
        volatile Node linkage;
        LinkedSoftKeySoftValueNode(int locator,
                                   Object key, Object value,
                                   CustomConcurrentHashMap cchm,
                                   Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class SoftKeySoftValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalSoftKeySoftValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedSoftKeySoftValueNode
                    (locator, key, value, cchm, linkage);
        }
    }

    abstract static class IntKeyNode implements Node {
        final int key;
        IntKeyNode(int locator, Object key) {
            this.key = ((Integer)key).intValue();
        }
        public final Object get() { return Integer.valueOf(key); }
        public final int getLocator() { return spreadHash(key); }
    }


    abstract static class IntKeySelfValueNode
        extends IntKeyNode {
        IntKeySelfValueNode(int locator, Object key) {
            super(locator, key);
        }
        public final Object getValue() { return Integer.valueOf(key); }
        public final void setValue(Object value) { }
        public final void onReclamation() { }
    }

    static final class TerminalIntKeySelfValueNode
        extends IntKeySelfValueNode {
        TerminalIntKeySelfValueNode(int locator, Object key) {
            super(locator, key);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedIntKeySelfValueNode
        extends IntKeySelfValueNode {
        volatile Node linkage;
        LinkedIntKeySelfValueNode(int locator, Object key,
                                     Node linkage) {
            super(locator, key);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class IntKeySelfValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalIntKeySelfValueNode
                    (locator, key);
            else
                return new LinkedIntKeySelfValueNode
                    (locator, key, linkage);
        }
    }

    abstract static class IntKeyStrongValueNode
        extends IntKeyNode {
        volatile Object value;
        IntKeyStrongValueNode(int locator, Object key, Object value) {
            super(locator, key);
            this.value = value;
        }
        public final Object getValue() { return value; }
        public final void setValue(Object value) { this.value = value; }
        public final void onReclamation() { }
    }

    static final class TerminalIntKeyStrongValueNode
        extends IntKeyStrongValueNode {
        TerminalIntKeyStrongValueNode(int locator,
                                         Object key, Object value) {
            super(locator, key, value);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedIntKeyStrongValueNode
        extends IntKeyStrongValueNode {
        volatile Node linkage;
        LinkedIntKeyStrongValueNode(int locator,
                                       Object key, Object value,
                                       Node linkage) {
            super(locator, key, value);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class IntKeyStrongValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalIntKeyStrongValueNode
                    (locator, key, value);
            else
                return new LinkedIntKeyStrongValueNode
                    (locator, key, value, linkage);
        }
    }

    abstract static class IntKeyIntValueNode
        extends IntKeyNode {
        volatile int value;
        IntKeyIntValueNode(int locator, Object key, Object value) {
            super(locator, key);
            this.value = ((Integer)value).intValue();
        }
        public final Object getValue() { return Integer.valueOf(value); }
        public final void setValue(Object value) {
            this.value = ((Integer)value).intValue();
        }
        public final void onReclamation() { }
    }

    static final class TerminalIntKeyIntValueNode
        extends IntKeyIntValueNode {
        TerminalIntKeyIntValueNode(int locator,
                                         Object key, Object value) {
            super(locator, key, value);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedIntKeyIntValueNode
        extends IntKeyIntValueNode {
        volatile Node linkage;
        LinkedIntKeyIntValueNode(int locator,
                                       Object key, Object value,
                                       Node linkage) {
            super(locator, key, value);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class IntKeyIntValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalIntKeyIntValueNode
                    (locator, key, value);
            else
                return new LinkedIntKeyIntValueNode
                    (locator, key, value, linkage);
        }
    }

    abstract static class IntKeyWeakValueNode
        extends IntKeyNode {
        volatile EmbeddedWeakReference valueRef;
        final CustomConcurrentHashMap cchm;
        IntKeyWeakValueNode(int locator, Object key, Object value,
                               CustomConcurrentHashMap cchm) {
            super(locator, key);
            this.cchm = cchm;
            if (value != null)
                this.valueRef = new EmbeddedWeakReference(value, this);
        }
        public final void onReclamation() {
            cchm.removeIfReclaimed(this);
        }
        public final Object getValue() {
            EmbeddedWeakReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedWeakReference(value, this);
        }
    }

    static final class TerminalIntKeyWeakValueNode
        extends IntKeyWeakValueNode {
        TerminalIntKeyWeakValueNode(int locator,
                                       Object key, Object value,
                                       CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedIntKeyWeakValueNode
        extends IntKeyWeakValueNode {
        volatile Node linkage;
        LinkedIntKeyWeakValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm,
                                     Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class IntKeyWeakValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalIntKeyWeakValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedIntKeyWeakValueNode
                    (locator, key, value, cchm, linkage);
        }
    }


    abstract static class IntKeySoftValueNode
        extends IntKeyNode {
        volatile EmbeddedSoftReference valueRef;
        final CustomConcurrentHashMap cchm;
        IntKeySoftValueNode(int locator, Object key, Object value,
                            CustomConcurrentHashMap cchm) {
            super(locator, key);
            this.cchm = cchm;
            if (value != null)
                this.valueRef = new EmbeddedSoftReference(value, this);
        }
        public final void onReclamation() {
            cchm.removeIfReclaimed(this);
        }
        public final Object getValue() {
            EmbeddedSoftReference vr = valueRef;
            return (vr == null) ? null : vr.get();
        }
        public final void setValue(Object value) {
            if (value == null)
                valueRef = null;
            else
                valueRef = new EmbeddedSoftReference(value, this);
        }
    }

    static final class TerminalIntKeySoftValueNode
        extends IntKeySoftValueNode {
        TerminalIntKeySoftValueNode(int locator,
                                       Object key, Object value,
                                       CustomConcurrentHashMap cchm) {
            super(locator, key, value, cchm);
        }
        public final Node getLinkage() { return null; }
        public final void setLinkage(Node linkage) { }
    }

    static final class LinkedIntKeySoftValueNode
        extends IntKeySoftValueNode {
        volatile Node linkage;
        LinkedIntKeySoftValueNode(int locator,
                                     Object key, Object value,
                                     CustomConcurrentHashMap cchm,
                                     Node linkage) {
            super(locator, key, value, cchm);
            this.linkage = linkage;
        }
        public final Node getLinkage() { return linkage; }
        public final void setLinkage(Node linkage) { this.linkage = linkage; }
    }

    static final class IntKeySoftValueNodeFactory
        implements NodeFactory, Serializable {
        private static final long serialVersionUID = 7249069346764182397L;
        public final Node newNode(int locator,
                                  Object key, Object value,
                                  CustomConcurrentHashMap cchm,
                                  Node linkage) {
            if (linkage == null)
                return new TerminalIntKeySoftValueNode
                    (locator, key, value, cchm);
            else
                return new LinkedIntKeySoftValueNode
                    (locator, key, value, cchm, linkage);
        }
    }



    // Tymczasowo niebezpieczna mechanika do wstępnego zwolnienia

    static final Unsafe UNSAFE;
    static final long tableBase;
    static final int tableShift;
    static final long segmentsBase;
    static final int segmentsShift;

    static {
        try {
            UNSAFE = getUnsafe();
            tableBase = UNSAFE.arrayBaseOffset(Node[].class);
            int scale = UNSAFE.arrayIndexScale(Node[].class);
            if ((scale & (scale - 1)) != 0)
                throw new Error("Skala typu danych nie jest potęgą dwóch");
            tableShift = 31 - Integer.numberOfLeadingZeros(scale);
            segmentsBase = UNSAFE.arrayBaseOffset(Segment[].class);
            scale = UNSAFE.arrayIndexScale(Segment[].class);
            if ((scale & (scale - 1)) != 0)
                throw new Error("Skala typu danych nie jest potęgą dwóch");
            segmentsShift = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Throwable e) {
            throw new RuntimeException("Nie można zainicjować wewnętrznych funkcji", e);
        }
    }

    // Ogrodzony magazyn w tablicy segmentów. Niepotrzebne, gdy są ogrodzenia
    static final void storeNode(Node[] table,
                                int i, Node r) {
        long nodeOffset = ((long) i << tableShift) + tableBase;
        UNSAFE.putOrderedObject(table, nodeOffset, r);
    }

    static final void storeSegment(Segment[] segs,
                                   int i, Segment s) {
        long segmentOffset = ((long) i << segmentsShift) + segmentsBase;
        UNSAFE.putOrderedObject(segs, segmentOffset, s);
    }

    /**
     * Zwraca sun.misc.Unsafe. Można stosować w zewnętrznym pakiecie  Zastąp 
     * prostym wywołaniem Unsafe.getUnsafe podczas integracji z JDK.
     *
     * @return a sun.misc.Unsafe
     */
    private static sun.misc.Unsafe getUnsafe() {
        try {
            return sun.misc.Unsafe.getUnsafe();
        } catch (SecurityException tryReflectionInstead) {}
        try {
            return java.security.AccessController.doPrivileged
            (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() {
                public sun.misc.Unsafe run() throws Exception {
                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
                    for (java.lang.reflect.Field f : k.getDeclaredFields()) {
                        f.setAccessible(true);
                        Object x = f.get(null);
                        if (k.isInstance(x))
                            return k.cast(x);
                    }
                    throw new NoSuchFieldError("Unsafe");
                }});
        } catch (java.security.PrivilegedActionException e) {
            throw new RuntimeException("Nie można zainicjować wewnętrznych funkcji",
                                       e.getCause());
        }
    }
}