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.concurrent.locks.Lock; 020import java.util.concurrent.locks.ReentrantLock; 021import java.util.function.Predicate; 022 023import org.apache.camel.util.function.TriConsumer; 024 025@Deprecated(since = "4.1.0") 026@SuppressWarnings("unchecked") 027public class DoubleMap<K1, K2, V> { 028 029 private static final double MAX_LOAD_FACTOR = 1.2; 030 private static final int MAX_TABLE_SIZE = 32768; 031 private static final int C1 = 0xcc9e2d51; 032 private static final int C2 = 0x1b873593; 033 034 static class Entry { 035 Object k1; 036 Object k2; 037 Object v; 038 Entry next; 039 } 040 041 private final Lock lock = new ReentrantLock(); 042 private Entry[] table; 043 private int mask; 044 045 public DoubleMap(int size) { 046 table = new Entry[closedTableSize(size)]; 047 mask = table.length - 1; 048 } 049 050 public V get(K1 k1, K2 k2) { 051 Entry[] table = this.table; 052 int mask = this.mask; 053 int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask; 054 for (Entry entry = table[index]; entry != null; entry = entry.next) { 055 if (k1 == entry.k1 && k2 == entry.k2) { 056 return (V) entry.v; 057 } 058 } 059 return null; 060 } 061 062 public void forEach(TriConsumer<K1, K2, V> consumer) { 063 Entry[] table = this.table; 064 for (Entry entry : table) { 065 while (entry != null) { 066 consumer.accept((K1) entry.k1, (K2) entry.k2, (V) entry.v); 067 entry = entry.next; 068 } 069 } 070 } 071 072 public boolean containsKey(K1 k1, K2 k2) { 073 Entry[] table = this.table; 074 int mask = this.mask; 075 int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask; 076 for (Entry entry = table[index]; entry != null; entry = entry.next) { 077 if (k1 == entry.k1 && k2 == entry.k2) { 078 return true; 079 } 080 } 081 return false; 082 } 083 084 public void put(K1 k1, K2 k2, V v) { 085 lock.lock(); 086 try { 087 Entry[] table = this.table; 088 int size = size() + 1; 089 int realSize = closedTableSize(size); 090 if (realSize <= table.length) { 091 realSize = table.length; 092 int index = smear(k1.hashCode() * 31 + k2.hashCode()) & (realSize - 1); 093 for (Entry oldEntry = table[index]; oldEntry != null; oldEntry = oldEntry.next) { 094 if (oldEntry.k1 == k1 && oldEntry.k2 == k2) { 095 oldEntry.v = v; 096 return; 097 } 098 } 099 Entry entry = new Entry(); 100 entry.k1 = k1; 101 entry.k2 = k2; 102 entry.v = v; 103 entry.next = table[index]; 104 table[index] = entry; 105 } else { 106 Entry[] newT = new Entry[realSize]; 107 int index = smear(k1.hashCode() * 31 + k2.hashCode()) & (realSize - 1); 108 Entry entry = new Entry(); 109 newT[index] = entry; 110 entry.k1 = k1; 111 entry.k2 = k2; 112 entry.v = v; 113 for (Entry oldEntry : table) { 114 while (oldEntry != null) { 115 if (k1 != oldEntry.k1 || k2 != oldEntry.k2) { 116 index = smear(oldEntry.k1.hashCode() * 31 + oldEntry.k2.hashCode()) & (realSize - 1); 117 Entry newEntry = new Entry(); 118 newEntry.k1 = oldEntry.k1; 119 newEntry.k2 = oldEntry.k2; 120 newEntry.v = oldEntry.v; 121 newEntry.next = newT[index]; 122 newT[index] = newEntry; 123 } 124 oldEntry = oldEntry.next; 125 } 126 } 127 this.table = newT; 128 this.mask = realSize - 1; 129 } 130 } finally { 131 lock.unlock(); 132 } 133 } 134 135 public boolean remove(K1 k1, K2 k2) { 136 lock.lock(); 137 try { 138 Entry[] table = this.table; 139 int mask = this.mask; 140 int index = smear(k1.hashCode() * 31 + k2.hashCode()) & mask; 141 Entry prevEntry = null; 142 for (Entry oldEntry = table[index]; oldEntry != null; prevEntry = oldEntry, oldEntry = oldEntry.next) { 143 if (oldEntry.k1 == k1 && oldEntry.k2 == k2) { 144 if (prevEntry == null) { 145 table[index] = oldEntry.next; 146 } else { 147 prevEntry.next = oldEntry.next; 148 } 149 return true; 150 } 151 } 152 return false; 153 } finally { 154 lock.unlock(); 155 } 156 } 157 158 public V getFirst(Predicate<K1> p1, Predicate<K2> p2) { 159 for (Entry entry : table) { 160 while (entry != null) { 161 if (p1.test((K1) entry.k1) && p2.test((K2) entry.k2)) { 162 return (V) entry.v; 163 } 164 entry = entry.next; 165 } 166 } 167 return null; 168 } 169 170 public int size() { 171 Entry[] table = this.table; 172 int n = 0; 173 if (table != null) { 174 for (Entry e : table) { 175 for (Entry c = e; c != null; c = c.next) { 176 n++; 177 } 178 } 179 } 180 return n; 181 } 182 183 public void clear() { 184 lock.lock(); 185 try { 186 this.table = new Entry[table.length]; 187 } finally { 188 lock.unlock(); 189 } 190 } 191 192 static int smear(int hashCode) { 193 return C2 * Integer.rotateLeft(hashCode * C1, 15); 194 } 195 196 static int closedTableSize(int expectedEntries) { 197 // Get the recommended table size. 198 // Round down to the nearest power of 2. 199 expectedEntries = Math.max(expectedEntries, 2); 200 int tableSize = Integer.highestOneBit(expectedEntries); 201 // Check to make sure that we will not exceed the maximum load factor. 202 if (expectedEntries > (int) (MAX_LOAD_FACTOR * tableSize)) { 203 tableSize <<= 1; 204 return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE; 205 } 206 return tableSize; 207 } 208 209}