001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.util;
018
019import java.util.function.Predicate;
020
021import org.apache.camel.util.function.TriConsumer;
022
023@Deprecated(since = "4.1.0")
024@SuppressWarnings("unchecked")
025public class DoubleMap<K1, K2, V> {
026
027    private static final double MAX_LOAD_FACTOR = 1.2;
028    private static final int MAX_TABLE_SIZE = 32768;
029    private static final int C1 = 0xcc9e2d51;
030    private static final int C2 = 0x1b873593;
031
032    static class Entry {
033        Object k1;
034        Object k2;
035        Object v;
036        Entry next;
037    }
038
039    private Entry[] table;
040    private int mask;
041
042    public DoubleMap(int size) {
043        table = new Entry[closedTableSize(size)];
044        mask = table.length - 1;
045    }
046
047    public V get(K1 k1, K2 k2) {
048        Entry[] table = this.table;
049        int mask = this.mask;
050        int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask;
051        for (Entry entry = table[index]; entry != null; entry = entry.next) {
052            if (k1 == entry.k1 && k2 == entry.k2) {
053                return (V) entry.v;
054            }
055        }
056        return null;
057    }
058
059    public void forEach(TriConsumer<K1, K2, V> consumer) {
060        Entry[] table = this.table;
061        for (Entry entry : table) {
062            while (entry != null) {
063                consumer.accept((K1) entry.k1, (K2) entry.k2, (V) entry.v);
064                entry = entry.next;
065            }
066        }
067    }
068
069    public boolean containsKey(K1 k1, K2 k2) {
070        Entry[] table = this.table;
071        int mask = this.mask;
072        int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask;
073        for (Entry entry = table[index]; entry != null; entry = entry.next) {
074            if (k1 == entry.k1 && k2 == entry.k2) {
075                return true;
076            }
077        }
078        return false;
079    }
080
081    public synchronized void put(K1 k1, K2 k2, V v) {
082        Entry[] table = this.table;
083        int size = size() + 1;
084        int realSize = closedTableSize(size);
085        if (realSize <= table.length) {
086            realSize = table.length;
087            int index = smear(k1.hashCode() * 31 + k2.hashCode()) & (realSize - 1);
088            for (Entry oldEntry = table[index]; oldEntry != null; oldEntry = oldEntry.next) {
089                if (oldEntry.k1 == k1 && oldEntry.k2 == k2) {
090                    oldEntry.v = v;
091                    return;
092                }
093            }
094            Entry entry = new Entry();
095            entry.k1 = k1;
096            entry.k2 = k2;
097            entry.v = v;
098            entry.next = table[index];
099            table[index] = entry;
100        } else {
101            Entry[] newT = new Entry[realSize];
102            int index = smear(k1.hashCode() * 31 + k2.hashCode()) & (realSize - 1);
103            Entry entry = new Entry();
104            newT[index] = entry;
105            entry.k1 = k1;
106            entry.k2 = k2;
107            entry.v = v;
108            for (Entry oldEntry : table) {
109                while (oldEntry != null) {
110                    if (k1 != oldEntry.k1 || k2 != oldEntry.k2) {
111                        index = smear(oldEntry.k1.hashCode() * 31 + oldEntry.k2.hashCode()) & (realSize - 1);
112                        Entry newEntry = new Entry();
113                        newEntry.k1 = oldEntry.k1;
114                        newEntry.k2 = oldEntry.k2;
115                        newEntry.v = oldEntry.v;
116                        newEntry.next = newT[index];
117                        newT[index] = newEntry;
118                    }
119                    oldEntry = oldEntry.next;
120                }
121            }
122            this.table = newT;
123            this.mask = realSize - 1;
124        }
125    }
126
127    public synchronized boolean remove(K1 k1, K2 k2) {
128        Entry[] table = this.table;
129        int mask = this.mask;
130        int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask;
131        Entry prevEntry = null;
132        for (Entry oldEntry = table[index]; oldEntry != null; prevEntry = oldEntry, oldEntry = oldEntry.next) {
133            if (oldEntry.k1 == k1 && oldEntry.k2 == k2) {
134                if (prevEntry == null) {
135                    table[index] = oldEntry.next;
136                } else {
137                    prevEntry.next = oldEntry.next;
138                }
139                return true;
140            }
141        }
142        return false;
143    }
144
145    public V getFirst(Predicate<K1> p1, Predicate<K2> p2) {
146        for (Entry entry : table) {
147            while (entry != null) {
148                if (p1.test((K1) entry.k1) && p2.test((K2) entry.k2)) {
149                    return (V) entry.v;
150                }
151                entry = entry.next;
152            }
153        }
154        return null;
155    }
156
157    public int size() {
158        Entry[] table = this.table;
159        int n = 0;
160        if (table != null) {
161            for (Entry e : table) {
162                for (Entry c = e; c != null; c = c.next) {
163                    n++;
164                }
165            }
166        }
167        return n;
168    }
169
170    public synchronized void clear() {
171        this.table = new Entry[table.length];
172    }
173
174    static int smear(int hashCode) {
175        return C2 * Integer.rotateLeft(hashCode * C1, 15);
176    }
177
178    static int closedTableSize(int expectedEntries) {
179        // Get the recommended table size.
180        // Round down to the nearest power of 2.
181        expectedEntries = Math.max(expectedEntries, 2);
182        int tableSize = Integer.highestOneBit(expectedEntries);
183        // Check to make sure that we will not exceed the maximum load factor.
184        if (expectedEntries > (int) (MAX_LOAD_FACTOR * tableSize)) {
185            tableSize <<= 1;
186            return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE;
187        }
188        return tableSize;
189    }
190
191}