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}