├── .gitignore ├── .travis.yml ├── LICENSE ├── NOTICE ├── README.md ├── compactmap ├── pom.xml └── src │ ├── main │ ├── java │ │ └── vlsi │ │ │ └── utils │ │ │ ├── CompactHashMap.java │ │ │ ├── CompactHashMapClass.java │ │ │ ├── CompactHashMapClassEmptyDefaults.java │ │ │ ├── CompactHashMapClassWithDefaults.java │ │ │ └── CompactHashMapDefaultValues.java │ └── resources │ │ └── META-INF │ │ ├── LICENSE │ │ └── NOTICE │ └── test │ └── java │ └── vlsi │ └── utils │ ├── CompactHashMapClassTest.java │ └── MapTest.java ├── jmh ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── vlsi │ └── compactmap │ └── MyBenchmark.java ├── jol ├── pom.xml └── src │ └── test │ └── java │ ├── CompactMapSizeTest.java │ └── NoDefaultsTest.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target 3 | .idea 4 | *.iml 5 | settings.xml 6 | .classpath.txt 7 | .fullclasspath.txt 8 | 9 | # eclipse 10 | .project 11 | .buildpath 12 | .classpath 13 | .settings 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: java 3 | dist: trusty 4 | 5 | before_cache: 6 | # No sense in caching current build artifacts 7 | rm -rf $HOME/.m2/repository/com/github/vlsi 8 | 9 | cache: 10 | directories: 11 | - $HOME/.m2/repository 12 | - $HOME/.ivy2/cache 13 | - $HOME/.sbt/boot/ 14 | 15 | install: 16 | - mvn -Djavac.target=$TRGT install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 17 | 18 | script: 19 | - mvn -Djavac.target=$TRGT test -B 20 | 21 | matrix: 22 | include: 23 | - jdk: oraclejdk8 24 | env: 25 | - JDK=8 26 | - TRGT=1.5 27 | - jdk: oraclejdk11 28 | env: 29 | - JDK=11 30 | - TRGT=1.6 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, and 11 | distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 14 | owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all other entities 17 | that control, are controlled by, or are under common control with that entity. 18 | For the purposes of this definition, "control" means (i) the power, direct or 19 | indirect, to cause the direction or management of such entity, whether by 20 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity exercising 24 | permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, including 27 | but not limited to software source code, documentation source, and configuration 28 | files. 29 | 30 | "Object" form shall mean any form resulting from mechanical transformation or 31 | translation of a Source form, including but not limited to compiled object code, 32 | generated documentation, and conversions to other media types. 33 | 34 | "Work" shall mean the work of authorship, whether in Source or Object form, made 35 | available under the License, as indicated by a copyright notice that is included 36 | in or attached to the work (an example is provided in the Appendix below). 37 | 38 | "Derivative Works" shall mean any work, whether in Source or Object form, that 39 | is based on (or derived from) the Work and for which the editorial revisions, 40 | annotations, elaborations, or other modifications represent, as a whole, an 41 | original work of authorship. For the purposes of this License, Derivative Works 42 | shall not include works that remain separable from, or merely link (or bind by 43 | name) to the interfaces of, the Work and Derivative Works thereof. 44 | 45 | "Contribution" shall mean any work of authorship, including the original version 46 | of the Work and any modifications or additions to that Work or Derivative Works 47 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 48 | by the copyright owner or by an individual or Legal Entity authorized to submit 49 | on behalf of the copyright owner. For the purposes of this definition, 50 | "submitted" means any form of electronic, verbal, or written communication sent 51 | to the Licensor or its representatives, including but not limited to 52 | communication on electronic mailing lists, source code control systems, and 53 | issue tracking systems that are managed by, or on behalf of, the Licensor for 54 | the purpose of discussing and improving the Work, but excluding communication 55 | that is conspicuously marked or otherwise designated in writing by the copyright 56 | owner as "Not a Contribution." 57 | 58 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 59 | of whom a Contribution has been received by Licensor and subsequently 60 | incorporated within the Work. 61 | 62 | 2. Grant of Copyright License. 63 | 64 | Subject to the terms and conditions of this License, each Contributor hereby 65 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 66 | irrevocable copyright license to reproduce, prepare Derivative Works of, 67 | publicly display, publicly perform, sublicense, and distribute the Work and such 68 | Derivative Works in Source or Object form. 69 | 70 | 3. Grant of Patent License. 71 | 72 | Subject to the terms and conditions of this License, each Contributor hereby 73 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 74 | irrevocable (except as stated in this section) patent license to make, have 75 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 76 | such license applies only to those patent claims licensable by such Contributor 77 | that are necessarily infringed by their Contribution(s) alone or by combination 78 | of their Contribution(s) with the Work to which such Contribution(s) was 79 | submitted. If You institute patent litigation against any entity (including a 80 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 81 | Contribution incorporated within the Work constitutes direct or contributory 82 | patent infringement, then any patent licenses granted to You under this License 83 | for that Work shall terminate as of the date such litigation is filed. 84 | 85 | 4. Redistribution. 86 | 87 | You may reproduce and distribute copies of the Work or Derivative Works thereof 88 | in any medium, with or without modifications, and in Source or Object form, 89 | provided that You meet the following conditions: 90 | 91 | You must give any other recipients of the Work or Derivative Works a copy of 92 | this License; and 93 | You must cause any modified files to carry prominent notices stating that You 94 | changed the files; and 95 | You must retain, in the Source form of any Derivative Works that You distribute, 96 | all copyright, patent, trademark, and attribution notices from the Source form 97 | of the Work, excluding those notices that do not pertain to any part of the 98 | Derivative Works; and 99 | If the Work includes a "NOTICE" text file as part of its distribution, then any 100 | Derivative Works that You distribute must include a readable copy of the 101 | attribution notices contained within such NOTICE file, excluding those notices 102 | that do not pertain to any part of the Derivative Works, in at least one of the 103 | following places: within a NOTICE text file distributed as part of the 104 | Derivative Works; within the Source form or documentation, if provided along 105 | with the Derivative Works; or, within a display generated by the Derivative 106 | Works, if and wherever such third-party notices normally appear. The contents of 107 | the NOTICE file are for informational purposes only and do not modify the 108 | License. You may add Your own attribution notices within Derivative Works that 109 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 110 | provided that such additional attribution notices cannot be construed as 111 | modifying the License. 112 | You may add Your own copyright statement to Your modifications and may provide 113 | additional or different license terms and conditions for use, reproduction, or 114 | distribution of Your modifications, or for any such Derivative Works as a whole, 115 | provided Your use, reproduction, and distribution of the Work otherwise complies 116 | with the conditions stated in this License. 117 | 118 | 5. Submission of Contributions. 119 | 120 | Unless You explicitly state otherwise, any Contribution intentionally submitted 121 | for inclusion in the Work by You to the Licensor shall be under the terms and 122 | conditions of this License, without any additional terms or conditions. 123 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 124 | any separate license agreement you may have executed with Licensor regarding 125 | such Contributions. 126 | 127 | 6. Trademarks. 128 | 129 | This License does not grant permission to use the trade names, trademarks, 130 | service marks, or product names of the Licensor, except as required for 131 | reasonable and customary use in describing the origin of the Work and 132 | reproducing the content of the NOTICE file. 133 | 134 | 7. Disclaimer of Warranty. 135 | 136 | Unless required by applicable law or agreed to in writing, Licensor provides the 137 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 138 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 139 | including, without limitation, any warranties or conditions of TITLE, 140 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 141 | solely responsible for determining the appropriateness of using or 142 | redistributing the Work and assume any risks associated with Your exercise of 143 | permissions under this License. 144 | 145 | 8. Limitation of Liability. 146 | 147 | In no event and under no legal theory, whether in tort (including negligence), 148 | contract, or otherwise, unless required by applicable law (such as deliberate 149 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 150 | liable to You for damages, including any direct, indirect, special, incidental, 151 | or consequential damages of any character arising as a result of this License or 152 | out of the use or inability to use the Work (including but not limited to 153 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 154 | any and all other commercial damages or losses), even if such Contributor has 155 | been advised of the possibility of such damages. 156 | 157 | 9. Accepting Warranty or Additional Liability. 158 | 159 | While redistributing the Work or Derivative Works thereof, You may choose to 160 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 161 | other liability obligations and/or rights consistent with this License. However, 162 | in accepting such obligations, You may act only on Your own behalf and on Your 163 | sole responsibility, not on behalf of any other Contributor, and only if You 164 | agree to indemnify, defend, and hold each Contributor harmless for any liability 165 | incurred by, or claims asserted against, such Contributor by reason of your 166 | accepting any such warranty or additional liability. 167 | 168 | END OF TERMS AND CONDITIONS 169 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | CompactMap, Copyright 2011, Vladimir Sitnikov 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/vlsi/compactmap.svg?branch=master)](https://travis-ci.org/vlsi/compactmap) 2 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.vlsi.compactmap/compactmap/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.vlsi.compactmap/compactmap) 3 | 4 | CompactHashMap 5 | ============== 6 | 7 | Usage 8 | ----- 9 | 10 | Add Maven dependency: 11 | ```xml 12 | 13 | com.github.vlsi.compactmap 14 | compactmap 15 | 1.3.0 16 | 17 | ``` 18 | 19 | Gradle: 20 | ```kotlin 21 | compile("com.github.vlsi.compactmap:compactmap:1.3.0") 22 | ``` 23 | 24 | About 25 | ----- 26 | This is a memory efficient alternative to HashMap. 27 | Main design goal is taken from "Fast Property Access" 28 | http://code.google.com/apis/v8/design.html#prop_access. 29 | 30 | This implementation however can store specific key-value pairs out of the map, 31 | so they do not consume memory when repeated in different maps. 32 | 33 | The expected memory consumption (8u40, 64 bit, compressed references) is as follows: 34 | 35 | # of elements CompactHashMap HashMap (with 1.0 fillFactor) 36 | 0 32 48 37 | 1 32 104 38 | 2 32 136 39 | 3 32 176 40 | 4 64 208 41 | 5 64 256 42 | 6 64 288 43 | 7 72 320 44 | 8 72 352 45 | 46 | In other words, the first three non default values consume the same 47 | 32 bytes, then map grows as 32 + 16 + 4 * (n-2) == 40 + 4 * n. 48 | Regular HashMap grows as 64 + 36 * n. 49 | 50 | The runtime of put and get is constant. 51 | The expected runtime is as follows (measured in hashmap and array accesses): 52 | 53 | best case worst case 54 | get 1 hashmap + 1 array 2 hashmap 55 | put 1 hashmap + 1 array 6 hashmap 56 | 57 | 58 | Sample 59 | ------ 60 | 61 | ``` java 62 | // Mark height->auto a default mapping entry, so it would not consume memory in CompactHashMaps 63 | CompactHashMapDefaultValues.add("height", "auto"); 64 | 65 | // Mark all values of width as default, so they would not consume memory in real maps 66 | CompactHashMapDefaultValues.add("width"); 67 | 68 | CompactHashMap map = new CompactHashMap(); 69 | map.put("height", "auto"); // does not consume memory in map 70 | map.put("width", "100%"); // does not consume memory in map either 71 | map.put("id", "myFirstButton"); // consumes some memory 72 | 73 | map.get("height"); // => "auto" 74 | map.get("width"); // => "100%" 75 | map.get("id"); // => "myFirstButton" 76 | 77 | map.put("height", "50px"); // consumes some memory (switches from default to custom) 78 | 79 | map.get("height"); // => "50px" 80 | ``` 81 | 82 | License 83 | ------- 84 | This library is distibuted under terms of Apache 2.0 License, see https://raw.githubusercontent.com/vlsi/compactmap/master/LICENSE 85 | 86 | Change log 87 | ---------- 88 | v2.0: 89 | * Change license: LGPL 2.0 -> Apache-2.0 90 | 91 | v1.3.0 92 | * Improvement: implement null keys 93 | * Improvement: `Map#toString` 94 | * Improvement: `Map#hashCode` + `equals` 95 | * Improvement: `Map.Entry#hashCode` + `equals` 96 | * Improvement: `Map.Entry#toString` 97 | * Improvement: `Map#containsValue` (it is slow but it works) 98 | * Test: use `guava-testlib` for `Map` implementation testing 99 | 100 | v1.2.1 101 | * Improvement: release to Maven Central 102 | * Improvement: fix EntrySet.remove method 103 | 104 | v1.2.0 105 | * Improvement: reduce size of hidden classes by using persistent dexx-collections. 106 | * Improvement: mavenize build 107 | * Switch to semantic versioning 108 | 109 | v1.1 110 | * Fix: #1 containKey returns true on non existing key 111 | * Fix: #2 size should account removed keys 112 | * Improvement: #3 Default values should be serialized as map 113 | 114 | Author 115 | ------ 116 | Vladimir Sitnikov 117 | -------------------------------------------------------------------------------- /compactmap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | parent 5 | com.github.vlsi.compactmap 6 | 2.0.2-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | compactmap 11 | jar 12 | 13 | Compact HashMap 14 | 15 | 16 | 17 | com.github.andrewoma.dexx 18 | dexx-collections 19 | 20 | 21 | org.junit.jupiter 22 | junit-jupiter 23 | 24 | 25 | org.junit.vintage 26 | junit-vintage-engine 27 | test 28 | 29 | 30 | com.google.guava 31 | guava-testlib 32 | 33 | 34 | -------------------------------------------------------------------------------- /compactmap/src/main/java/vlsi/utils/CompactHashMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import java.io.IOException; 20 | import java.io.Serializable; 21 | import java.util.Collection; 22 | import java.util.Iterator; 23 | import java.util.Map; 24 | import java.util.Set; 25 | 26 | /** 27 | * Hash table implementation modelled after memory efficient 28 | * V8's Fast 29 | * Property Access. This class however can store specific key-value pairs out of 30 | * the map, so they do not consume memory when repeated in different maps. 31 | * This implementation permits null keys and 32 | * null values. This map makes no guarantees as to the order of the map. 33 | * 34 | *

This implementation provides constant access time for the basic 35 | * operations (get and put). The get operation 36 | * does not create objects. put creates array objects when resizing 37 | * is required.

38 | * 39 | *

The expected runtime is as follows (measured in hashmap and array accesses): 40 | * best case worst case 41 | * get 1 hashmap + 1 array 2 hashmap 42 | * put 1 hashmap + 1 array 6 hashmap 43 | *

44 | * 45 | *

The expected memory consumption (8u40, 64 bit, compressed references) is as follows: 46 | * # of elements CompactHashMap HashMap (with 1.0 fillFactor) 47 | * 0 32 48 48 | * 1 32 104 49 | * 2 32 136 50 | * 3 32 176 51 | * 4 64 208 52 | * 5 64 256 53 | * 6 64 288 54 | * 7 72 320 55 | * 8 72 352 56 | * 57 | * In other words, the first three non default values consume the same 58 | * 32 bytes, then map grows as 32 + 16 + 4 * (n-2) == 40 + 4 * n. 59 | * Regular HashMap grows as 64 + 36 * n. 60 | *

61 | * 62 | * 63 | *

Note that map keys must be reused (you should not use unique 64 | * objects for keys), otherwise you will run out of memory.

65 | * 66 | *

Note that this implementation is not synchronized 67 | * If multiple threads access the map concurrently, and at least one 68 | * of the threads modifies the map, it must be synchronized 69 | * externally. 70 | *

71 | * 72 | * @author Vladimir Sitnikov 73 | * @param the type of keys maintained by this map 74 | * @param the type of mapped values 75 | */ 76 | public class CompactHashMap implements Map, Serializable { 77 | private static final long serialVersionUID = -7720507706954394566L; 78 | 79 | CompactHashMapClass klass = CompactHashMapClass.EMPTY; 80 | Object v1, v2, v3; 81 | 82 | public int size() { 83 | return klass.size(this); 84 | } 85 | 86 | public boolean isEmpty() { 87 | return size() == 0; 88 | } 89 | 90 | public boolean containsKey(Object key) { 91 | return klass.containsKey(this, key); 92 | } 93 | 94 | public boolean containsValue(Object value) { 95 | return values().contains(value); 96 | } 97 | 98 | public V get(Object key) { 99 | return klass.get(this, (K) key); 100 | } 101 | 102 | public V put(K key, V value) { 103 | return klass.put(this, key, value); 104 | } 105 | 106 | public V putOrRemove(K key, Object value) { 107 | return klass.put(this, key, value); 108 | } 109 | 110 | public V remove(Object key) { 111 | return klass.put(this, (K) key, CompactHashMapClass.REMOVED_OBJECT); 112 | } 113 | 114 | public void putAll(Map m) { 115 | for (Entry entry : m.entrySet()) { 116 | put(entry.getKey(), entry.getValue()); 117 | } 118 | } 119 | 120 | public void clear() { 121 | klass = CompactHashMapClass.EMPTY; 122 | v1 = v2 = v3 = null; 123 | } 124 | 125 | public Set keySet() { 126 | return klass.keySet(this); 127 | } 128 | 129 | public Collection values() { 130 | return klass.values(this); 131 | } 132 | 133 | public Set> entrySet() { 134 | return klass.entrySet(this); 135 | } 136 | 137 | public boolean equals(Object o) { 138 | if (o == this) { 139 | return true; 140 | } 141 | 142 | if (!(o instanceof Map)) { 143 | return false; 144 | } 145 | 146 | Map m = (Map) o; 147 | if (m.size() != size()) 148 | return false; 149 | 150 | for (Entry e : entrySet()) { 151 | K key = e.getKey(); 152 | V value = e.getValue(); 153 | 154 | if (value == null) { 155 | if (m.get(key) != null || !m.containsKey(key)) { 156 | return false; 157 | } 158 | } else { 159 | if (!value.equals(m.get(key))) { 160 | return false; 161 | } 162 | } 163 | } 164 | 165 | return true; 166 | } 167 | 168 | @Override 169 | public String toString() { 170 | Iterator> it = entrySet().iterator(); 171 | if (!it.hasNext()) 172 | return "{}"; 173 | 174 | StringBuilder sb = new StringBuilder(); 175 | sb.append('{'); 176 | while (it.hasNext()) { 177 | Entry e = it.next(); 178 | K key = e.getKey(); 179 | V value = e.getValue(); 180 | sb.append(key).append('=').append(value); 181 | sb.append(',').append(' '); 182 | } 183 | sb.setLength(sb.length() - 2); 184 | return sb.append('}').toString(); 185 | } 186 | 187 | public int hashCode() { 188 | int h = 0; 189 | for (Entry entry : entrySet()) { 190 | h += entry.hashCode(); 191 | } 192 | return h; 193 | } 194 | 195 | private void writeObject(java.io.ObjectOutputStream s) throws IOException { 196 | klass.serialize(this, s); 197 | } 198 | 199 | private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { 200 | CompactHashMapClass.deserialize(this, s); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /compactmap/src/main/java/vlsi/utils/CompactHashMapClass.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import com.github.andrewoma.dexx.collection.Pair; 20 | 21 | import java.io.IOException; 22 | import java.io.ObjectInputStream; 23 | import java.io.ObjectOutputStream; 24 | import java.util.*; 25 | 26 | abstract class CompactHashMapClass { 27 | public static final CompactHashMapClass EMPTY = new CompactHashMapClassEmptyDefaults( 28 | new com.github.andrewoma.dexx.collection.HashMap()); 29 | 30 | /** 31 | * Enum to represent "value was removed" in the serialized representation of the map. 32 | */ 33 | enum RemovedObjectMarker { 34 | INSTANCE; 35 | } 36 | 37 | final com.github.andrewoma.dexx.collection.Map key2slot; // Immutable 38 | 39 | // This value is used as a marker of deleted object 40 | // "new String" is required to avoid clashing with regular strings 41 | public static final String REMOVED_OBJECT = new String("Non existing mapping value"); 42 | 43 | // dexx does not support null, so we wrap null 44 | private static final Object NULL = new Object(); 45 | 46 | public CompactHashMapClass(com.github.andrewoma.dexx.collection.Map key2slot) { 47 | this.key2slot = key2slot; 48 | } 49 | 50 | private K maskNull(K key) { 51 | return key == null ? (K) NULL : key; 52 | } 53 | 54 | private K unmaskNull(K key) { 55 | return key == NULL ? null : key; 56 | } 57 | 58 | protected Map getDefaultValues() { 59 | return Collections.emptyMap(); 60 | } 61 | 62 | protected abstract CompactHashMapClassEmptyDefaults getMapWithEmptyDefaults(); 63 | 64 | public V get(CompactHashMap map, K key) { 65 | Object result = getInternal(map, key); 66 | return result != REMOVED_OBJECT ? (V) result : null; 67 | } 68 | 69 | private Object getInternal(CompactHashMap map, Object key) { 70 | K nonNullKey = maskNull((K) key); 71 | final Integer slot = key2slot.get(nonNullKey); 72 | if (slot == null) 73 | return getDefaultValues().get(nonNullKey); 74 | 75 | return getValueFromSlot(map, slot); 76 | } 77 | 78 | protected static Object getValueFromSlot(CompactHashMap map, int slot) { 79 | switch (slot) { 80 | case -2: 81 | return map.v3; 82 | case -1: 83 | return map.v2; 84 | case 0: 85 | // Maps with fewer than 3 keys store their slot 0 in v1 86 | if (map.klass.key2slot.size() <= 3) { 87 | return map.v1; 88 | } 89 | } 90 | return ((Object[]) map.v1)[slot]; 91 | } 92 | 93 | public V put(CompactHashMap map, K key, Object value) { 94 | K nonNullKey = maskNull(key); 95 | Integer slot = key2slot.get(nonNullKey); 96 | Object prevValue = REMOVED_OBJECT; 97 | if (slot == null) { 98 | prevValue = getDefaultValues().get(nonNullKey); 99 | 100 | // Try put value as "default" 101 | Map newDef = CompactHashMapDefaultValues.getNewDefaultValues(getDefaultValues(), nonNullKey, value); 102 | if (newDef != null) { 103 | map.klass = getMapWithEmptyDefaults().getNewDefaultClass(newDef); 104 | return (V) prevValue; 105 | } 106 | 107 | if (value == REMOVED_OBJECT) 108 | return (V) prevValue; 109 | // The value is not default -- put using regular way 110 | slot = createNewSlot(map, nonNullKey); 111 | } 112 | 113 | switch (slot) { 114 | case -1: 115 | if (prevValue == REMOVED_OBJECT) 116 | prevValue = map.v2; 117 | map.v2 = value; 118 | break; 119 | case -2: 120 | if (prevValue == REMOVED_OBJECT) 121 | prevValue = map.v3; 122 | map.v3 = value; 123 | break; 124 | default: 125 | if (slot == 0 && key2slot.size() <= 3) { 126 | if (prevValue == REMOVED_OBJECT) 127 | prevValue = map.v1; 128 | map.v1 = value; 129 | break; 130 | } 131 | 132 | Object[] array = (Object[]) map.v1; 133 | if (prevValue == REMOVED_OBJECT) 134 | prevValue = array[slot]; 135 | array[slot] = value; 136 | break; 137 | } 138 | 139 | return (V) prevValue; 140 | } 141 | 142 | private Integer createNewSlot(CompactHashMap map, K key) { 143 | final CompactHashMapClass nextKlass = getMapWithEmptyDefaults().getNextKlass(key, getDefaultValues()); 144 | map.klass = nextKlass; 145 | 146 | int prevSize = key2slot.size(); 147 | 148 | if (prevSize == 3) { 149 | // Array length should be odd to play well with 8 byte alignment of object size 150 | // 1.5 refs (object header) + 1 int (array length) + n*length refs (contents) 151 | Object[] array = new Object[4]; 152 | array[0] = map.v1; 153 | map.v1 = array; 154 | } else if (prevSize > 3) { 155 | Object[] array = (Object[]) map.v1; 156 | if (array.length < prevSize - 1) { 157 | int newSize = array.length * 3 / 2; 158 | newSize += newSize & 1; // If odd, round to next even 159 | Object[] newArray = new Object[newSize]; 160 | System.arraycopy(array, 0, newArray, 0, array.length); 161 | map.v1 = newArray; 162 | } 163 | } 164 | 165 | return nextKlass.key2slot.get(key); 166 | } 167 | 168 | public int size(CompactHashMap map) { 169 | return key2slot.size() + getDefaultValues().size() - removedSlotsCount(map); 170 | } 171 | 172 | private int removedSlotsCount(CompactHashMap map) { 173 | int emptySlots = 0; 174 | switch (key2slot.size()) { 175 | default: // more than 3 176 | for (Object o : (Object[]) map.v1) { 177 | if (o == REMOVED_OBJECT) emptySlots++; 178 | } 179 | /* fall through */ 180 | case 3: // v1 is filled after v2 181 | if (map.v1 == REMOVED_OBJECT) emptySlots++; 182 | /* fall through */ 183 | case 2: // v2 is filled after v3 184 | if (map.v2 == REMOVED_OBJECT) emptySlots++; 185 | /* fall through */ 186 | case 1: // v3 is filled the first 187 | if (map.v3 == REMOVED_OBJECT) emptySlots++; 188 | case 0: 189 | } 190 | 191 | return emptySlots; 192 | } 193 | 194 | public boolean containsKey(CompactHashMap map, Object key) { 195 | // We cannot use plain getInternal here since we will be unable to distinguish 196 | // existing, but null default value 197 | K nonNullKey = maskNull((K) key); 198 | final Integer slot = key2slot.get(nonNullKey); 199 | if (slot == null) 200 | return getDefaultValues().containsKey(nonNullKey); 201 | 202 | return getValueFromSlot(map, slot) != REMOVED_OBJECT; 203 | } 204 | 205 | public Set keySet(CompactHashMap map) { 206 | return new CompactHashMapClass.KeySet(map); 207 | } 208 | 209 | public Set values(CompactHashMap map) { 210 | return new CompactHashMapClass.Values(map); 211 | } 212 | 213 | public Set> entrySet(CompactHashMap map) { 214 | return new CompactHashMapClass.EntrySet(map); 215 | } 216 | 217 | public void serialize(final CompactHashMap map, final ObjectOutputStream s) throws IOException { 218 | // We serialize default and non default values separately 219 | // That makes serialized representation more compact when several maps share defaults 220 | int ownKeys = key2slot.size(); 221 | s.writeInt(ownKeys); 222 | // Write keys in the order they were added to the map, so deserialization reuses key2slot instances 223 | if (ownKeys > 0) { 224 | // Slots are always -2..(map.size-2), so we do not need sort 225 | final Object[] keys = new Object[ownKeys]; 226 | key2slot.forEach(new com.github.andrewoma.dexx.collection.Function, Void>() { 227 | public Void invoke(Pair entry) { 228 | keys[entry.component2() + 2] = entry.component1(); 229 | return null; 230 | } 231 | }); 232 | for (int i = 0; i < keys.length; i++) { 233 | Object key = keys[i]; 234 | Object value = getValueFromSlot(map, i - 2); 235 | s.writeObject(unmaskNull((K) key)); 236 | // We write REMOVED_OBJECT as well to keep the same key2slot when deserializing 237 | s.writeObject(value == REMOVED_OBJECT ? RemovedObjectMarker.INSTANCE : value); 238 | } 239 | } 240 | 241 | // Serialize default values as separate map 242 | s.writeObject(getDefaultValues()); 243 | } 244 | 245 | public static void deserialize(CompactHashMap map, ObjectInputStream s) throws IOException, ClassNotFoundException { 246 | int size = s.readInt(); 247 | map.klass = CompactHashMapClass.EMPTY; 248 | 249 | for (int i = 0; i < size; i++) { 250 | K key = (K) s.readObject(); 251 | V value = (V) s.readObject(); 252 | if (value == RemovedObjectMarker.INSTANCE) { 253 | // We cannot use put(key, REMOVED_OBJECT) as the map would just ignore it 254 | // We need to add an entry first so it allocates a new slot 255 | map.put(key, null); 256 | map.remove(key); 257 | } else { 258 | map.put(key, value); 259 | } 260 | } 261 | 262 | Map defaults = (Map) s.readObject(); 263 | // TODO: optimize to CompactHashMapClassEmptyDefaults.getNewDefaultClass 264 | // Current implementation of getNewDefaultClass relies on identity equality, thus it does not fit 265 | for (Map.Entry entry : defaults.entrySet()) { 266 | map.put(entry.getKey(), entry.getValue()); 267 | } 268 | } 269 | 270 | static class KeySet extends AbstractSet { 271 | private final CompactHashMap map; 272 | 273 | public KeySet(CompactHashMap map) { 274 | this.map = map; 275 | } 276 | 277 | @Override 278 | public int size() { 279 | return map.size(); 280 | } 281 | 282 | @Override 283 | public boolean contains(Object o) { 284 | return map.containsKey(o); 285 | } 286 | 287 | @Override 288 | public boolean remove(Object o) { 289 | return map.remove(o) != null; // TODO: support null as "previous" value 290 | } 291 | 292 | @Override 293 | public Iterator iterator() { 294 | return new KeyIterator(map); 295 | } 296 | 297 | @Override 298 | public void clear() { 299 | map.clear(); 300 | } 301 | } 302 | 303 | static class Values extends AbstractSet { 304 | private final CompactHashMap map; 305 | 306 | public Values(CompactHashMap map) { 307 | this.map = map; 308 | } 309 | 310 | @Override 311 | public int size() { 312 | return map.size(); 313 | } 314 | 315 | @Override 316 | public Iterator iterator() { 317 | return new ValueIterator(map); 318 | } 319 | 320 | @Override 321 | public void clear() { 322 | map.clear(); 323 | } 324 | } 325 | 326 | static class EntrySet extends AbstractSet> { 327 | private final CompactHashMap map; 328 | 329 | public EntrySet(CompactHashMap map) { 330 | this.map = map; 331 | } 332 | 333 | @Override 334 | public Iterator> iterator() { 335 | return new EntryIterator(map); 336 | } 337 | 338 | @Override 339 | public boolean contains(Object o) { 340 | if (!(o instanceof Map.Entry)) 341 | return false; 342 | Map.Entry e = (Map.Entry) o; 343 | K key = e.getKey(); 344 | V value = e.getValue(); 345 | V ourValue = map.get(key); 346 | if (value == null) { 347 | return ourValue == null && map.containsKey(key); 348 | } 349 | return value.equals(ourValue); 350 | } 351 | 352 | @Override 353 | public boolean remove(Object o) { 354 | if (!(o instanceof Map.Entry)) 355 | return false; 356 | Map.Entry e = (Map.Entry) o; 357 | return map.remove(e.getKey()) != null; // TODO: support "return true" when value was null 358 | } 359 | 360 | @Override 361 | public int size() { 362 | return map.size(); 363 | } 364 | 365 | @Override 366 | public void clear() { 367 | map.clear(); 368 | } 369 | } 370 | 371 | static abstract class HashIterator implements Iterator { 372 | boolean defValues = true; 373 | private final CompactHashMap map; 374 | Iterator it; 375 | Map.Entry current, next; 376 | 377 | public HashIterator(CompactHashMap map) { 378 | this.map = map; 379 | if (map.isEmpty()) return; 380 | this.it = map.klass.getDefaultValues().entrySet().iterator(); 381 | advance(); 382 | } 383 | 384 | private void advance() { 385 | if (!it.hasNext() && defValues) { 386 | defValues = false; 387 | it = map.klass.key2slot.asMap().entrySet().iterator(); 388 | } 389 | 390 | if (!it.hasNext()) { 391 | next = null; 392 | return; 393 | } 394 | 395 | while (it.hasNext()) { 396 | Map.Entry entry = (Map.Entry) it.next(); 397 | V value; 398 | if (defValues) 399 | value = (V) entry.getValue(); 400 | else { 401 | value = (V) getValueFromSlot(map, (Integer) entry.getValue()); 402 | if (value == REMOVED_OBJECT) continue; 403 | } 404 | next = new SimpleEntry(map, (K) entry.getKey(), value); 405 | return; 406 | } 407 | next = null; 408 | } 409 | 410 | public boolean hasNext() { 411 | return next != null; 412 | } 413 | 414 | public Map.Entry nextEntry() { 415 | if (next == null) 416 | throw new NoSuchElementException(); 417 | current = next; 418 | advance(); 419 | return current; 420 | } 421 | 422 | public void remove() { 423 | if (current == null) { 424 | throw new IllegalStateException(); 425 | } 426 | map.remove(current.getKey()); 427 | current = null; 428 | } 429 | } 430 | 431 | 432 | static class KeyIterator extends HashIterator { 433 | public KeyIterator(CompactHashMap kvCompactMap) { 434 | super(kvCompactMap); 435 | } 436 | 437 | public K next() { 438 | return nextEntry().getKey(); 439 | } 440 | } 441 | 442 | static class ValueIterator extends HashIterator { 443 | public ValueIterator(CompactHashMap kvCompactMap) { 444 | super(kvCompactMap); 445 | } 446 | 447 | public V next() { 448 | return nextEntry().getValue(); 449 | } 450 | } 451 | 452 | static class EntryIterator extends HashIterator> { 453 | public EntryIterator(CompactHashMap kvCompactMap) { 454 | super(kvCompactMap); 455 | } 456 | 457 | public Map.Entry next() { 458 | return nextEntry(); 459 | } 460 | } 461 | 462 | static class SimpleEntry implements Map.Entry { 463 | final K key; 464 | V value; 465 | private final CompactHashMap map; 466 | 467 | public SimpleEntry(CompactHashMap map, K key, V value) { 468 | this.map = map; 469 | this.key = key; 470 | this.value = value; 471 | } 472 | 473 | public K getKey() { 474 | return map.klass.unmaskNull(key); 475 | } 476 | 477 | public V getValue() { 478 | return value; 479 | } 480 | 481 | public V setValue(V value) { 482 | this.value = value; 483 | return map.put(key, value); 484 | } 485 | 486 | private static boolean eq(Object o1, Object o2) { 487 | return o1 == null ? o2 == null : o1.equals(o2); 488 | } 489 | 490 | public boolean equals(Object o) { 491 | if (!(o instanceof Map.Entry)) { 492 | return false; 493 | } 494 | Map.Entry e = (Map.Entry) o; 495 | 496 | return eq(getKey(), e.getKey()) && eq(value, e.getValue()); 497 | } 498 | 499 | public int hashCode() { 500 | return (key == NULL ? 0 : key.hashCode()) ^ 501 | (value == null ? 0 : value.hashCode()); 502 | } 503 | 504 | @Override 505 | public String toString() { 506 | return map.klass.unmaskNull(key) + "=" + value; 507 | } 508 | } 509 | 510 | } 511 | -------------------------------------------------------------------------------- /compactmap/src/main/java/vlsi/utils/CompactHashMapClassEmptyDefaults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.IdentityHashMap; 22 | import java.util.Map; 23 | 24 | /** 25 | * This map represents CompactHashMapClass that has no default values (it can have nonempty key2slot). 26 | * It is used to determine the right CompactHashMapClass given the desired defaultValues map. 27 | * 28 | * @author Vladimir Sitnikov 29 | * @param the type of keys maintained by this map 30 | * @param the type of mapped values 31 | */ 32 | class CompactHashMapClassEmptyDefaults extends CompactHashMapClass { 33 | private Map> key2newKlass; 34 | private Map, CompactHashMapClass> defValues2Klass; 35 | 36 | public CompactHashMapClassEmptyDefaults(com.github.andrewoma.dexx.collection.Map key2Slot) { 37 | super(key2Slot); 38 | } 39 | 40 | @Override 41 | protected CompactHashMapClassEmptyDefaults getMapWithEmptyDefaults() { 42 | return this; 43 | } 44 | 45 | protected CompactHashMapClass getNewDefaultClass(Map newDef) { 46 | CompactHashMapClass newClass; 47 | if (newDef == null || newDef.isEmpty()) 48 | return this; 49 | synchronized (this) { 50 | Map, CompactHashMapClass> defValues2Klass = this.defValues2Klass; 51 | if (defValues2Klass == null) 52 | this.defValues2Klass = defValues2Klass = 53 | new IdentityHashMap, CompactHashMapClass>(); 54 | 55 | newClass = defValues2Klass.get(newDef); 56 | if (newClass == null) { 57 | newClass = new CompactHashMapClassWithDefaults(key2slot, newDef, this); 58 | defValues2Klass.put(newDef, newClass); 59 | } 60 | } 61 | return newClass; 62 | } 63 | 64 | protected CompactHashMapClass getNextKlass(K key, Map defaultValues) { 65 | CompactHashMapClassEmptyDefaults newKlass = null; 66 | synchronized (this) { 67 | Map> key2newKlass = this.key2newKlass; 68 | if (key2newKlass != null) 69 | newKlass = key2newKlass.get(key); 70 | } 71 | 72 | if (defaultValues.containsKey(key)) 73 | defaultValues = CompactHashMapDefaultValues.getNewDefaultValues(defaultValues, key, REMOVED_OBJECT); 74 | 75 | if (newKlass != null) 76 | return newKlass.getNewDefaultClass(defaultValues); 77 | 78 | int size = key2slot.size(); 79 | com.github.andrewoma.dexx.collection.Map newKey2slot = key2slot; 80 | 81 | newKey2slot = newKey2slot.put(key, size - 2); 82 | 83 | newKlass = new CompactHashMapClassEmptyDefaults(newKey2slot); 84 | synchronized (this) { 85 | if (key2newKlass == null) { 86 | key2newKlass = Collections.singletonMap(key, newKlass); 87 | } else { 88 | final CompactHashMapClassEmptyDefaults anotherNewKlass = key2newKlass.get(key); 89 | 90 | if (anotherNewKlass != null) 91 | newKlass = anotherNewKlass; 92 | else { 93 | if (key2newKlass.size() == 1) { 94 | key2newKlass = new HashMap>(key2newKlass); 95 | } 96 | key2newKlass.put(key, newKlass); 97 | } 98 | } 99 | } 100 | 101 | return newKlass.getNewDefaultClass(defaultValues); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /compactmap/src/main/java/vlsi/utils/CompactHashMapClassWithDefaults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import java.util.Map; 20 | 21 | class CompactHashMapClassWithDefaults extends CompactHashMapClass { 22 | private final Map defaultValues; 23 | private final CompactHashMapClassEmptyDefaults mapClassEmptyDefaults; 24 | 25 | public CompactHashMapClassWithDefaults( 26 | com.github.andrewoma.dexx.collection.Map key2Slot, 27 | Map defaultValues, 28 | CompactHashMapClassEmptyDefaults mapClassEmptyDefaults) { 29 | super(key2Slot); 30 | this.defaultValues = defaultValues; 31 | this.mapClassEmptyDefaults = mapClassEmptyDefaults; 32 | } 33 | 34 | @Override 35 | protected Map getDefaultValues() { 36 | return defaultValues; 37 | } 38 | 39 | @Override 40 | protected CompactHashMapClassEmptyDefaults getMapWithEmptyDefaults() { 41 | return mapClassEmptyDefaults; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /compactmap/src/main/java/vlsi/utils/CompactHashMapDefaultValues.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import java.util.HashMap; 20 | import java.util.IdentityHashMap; 21 | import java.util.LinkedHashMap; 22 | import java.util.Map; 23 | import java.util.concurrent.locks.Lock; 24 | import java.util.concurrent.locks.ReadWriteLock; 25 | import java.util.concurrent.locks.ReentrantReadWriteLock; 26 | 27 | public class CompactHashMapDefaultValues { 28 | // Key -> Value -> OldMap -> NewMap 29 | private static Map>> defaultValues 30 | = new HashMap>>(); 31 | 32 | private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 33 | private static Lock readLock = readWriteLock.readLock(); 34 | private static Lock writeLock = readWriteLock.writeLock(); 35 | 36 | public static final String ALL_VALUES_MATCH = new String("All values match"); 37 | 38 | public static void clear() { 39 | writeLock.lock(); 40 | try { 41 | defaultValues.clear(); 42 | } finally { 43 | writeLock.unlock(); 44 | } 45 | } 46 | 47 | public static boolean add(Object key) { 48 | return add(key, ALL_VALUES_MATCH); 49 | } 50 | 51 | public static boolean add(Object key, Object value) { 52 | writeLock.lock(); 53 | try { 54 | Map> m = defaultValues.get(key); 55 | if (m == null) 56 | defaultValues.put(key, m = new HashMap>()); 57 | 58 | if (m.get(value) != null) 59 | return false; // The value is already marked as default 60 | 61 | m.put(value, new IdentityHashMap()); 62 | return true; 63 | } finally { 64 | writeLock.unlock(); 65 | } 66 | } 67 | 68 | public static Map getNewDefaultValues(Map prevDefaultValues, K key, Object value) { 69 | final Map> m; 70 | Map identityOld2New; 71 | 72 | readLock.lock(); 73 | try { 74 | m = defaultValues.get(key); 75 | if (m == null) return null; // The key is not default 76 | 77 | identityOld2New = m.get(value); 78 | if (identityOld2New == null) { 79 | if (value != CompactHashMapClass.REMOVED_OBJECT && m.get(ALL_VALUES_MATCH) == null) 80 | return null; // The value is not default 81 | } else { 82 | Map newMap = identityOld2New.get(prevDefaultValues); 83 | if (newMap != null) return newMap; 84 | } 85 | } finally { 86 | readLock.unlock(); 87 | } 88 | 89 | // Keep the order of entries in the default values, so we have a consistent subset of "default class" 90 | Map newMap = new LinkedHashMap((int) ((prevDefaultValues.size() + 1) / 0.75f)); 91 | 92 | newMap.putAll(prevDefaultValues); 93 | 94 | if (value == CompactHashMapClass.REMOVED_OBJECT) 95 | newMap.remove(key); 96 | else 97 | newMap.put(key, (V) value); 98 | 99 | writeLock.lock(); 100 | try { 101 | if (identityOld2New == null) { 102 | identityOld2New = m.get(value); 103 | if (identityOld2New == null) 104 | m.put(value, identityOld2New = new IdentityHashMap()); 105 | } 106 | 107 | final Map anotherNewMap = identityOld2New.get(prevDefaultValues); 108 | if (anotherNewMap != null) return anotherNewMap; // In case another thread has just created new map 109 | 110 | identityOld2New.put(prevDefaultValues, newMap); 111 | return newMap; 112 | } finally { 113 | writeLock.unlock(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /compactmap/src/main/resources/META-INF/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, and 11 | distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 14 | owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all other entities 17 | that control, are controlled by, or are under common control with that entity. 18 | For the purposes of this definition, "control" means (i) the power, direct or 19 | indirect, to cause the direction or management of such entity, whether by 20 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity exercising 24 | permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, including 27 | but not limited to software source code, documentation source, and configuration 28 | files. 29 | 30 | "Object" form shall mean any form resulting from mechanical transformation or 31 | translation of a Source form, including but not limited to compiled object code, 32 | generated documentation, and conversions to other media types. 33 | 34 | "Work" shall mean the work of authorship, whether in Source or Object form, made 35 | available under the License, as indicated by a copyright notice that is included 36 | in or attached to the work (an example is provided in the Appendix below). 37 | 38 | "Derivative Works" shall mean any work, whether in Source or Object form, that 39 | is based on (or derived from) the Work and for which the editorial revisions, 40 | annotations, elaborations, or other modifications represent, as a whole, an 41 | original work of authorship. For the purposes of this License, Derivative Works 42 | shall not include works that remain separable from, or merely link (or bind by 43 | name) to the interfaces of, the Work and Derivative Works thereof. 44 | 45 | "Contribution" shall mean any work of authorship, including the original version 46 | of the Work and any modifications or additions to that Work or Derivative Works 47 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 48 | by the copyright owner or by an individual or Legal Entity authorized to submit 49 | on behalf of the copyright owner. For the purposes of this definition, 50 | "submitted" means any form of electronic, verbal, or written communication sent 51 | to the Licensor or its representatives, including but not limited to 52 | communication on electronic mailing lists, source code control systems, and 53 | issue tracking systems that are managed by, or on behalf of, the Licensor for 54 | the purpose of discussing and improving the Work, but excluding communication 55 | that is conspicuously marked or otherwise designated in writing by the copyright 56 | owner as "Not a Contribution." 57 | 58 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 59 | of whom a Contribution has been received by Licensor and subsequently 60 | incorporated within the Work. 61 | 62 | 2. Grant of Copyright License. 63 | 64 | Subject to the terms and conditions of this License, each Contributor hereby 65 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 66 | irrevocable copyright license to reproduce, prepare Derivative Works of, 67 | publicly display, publicly perform, sublicense, and distribute the Work and such 68 | Derivative Works in Source or Object form. 69 | 70 | 3. Grant of Patent License. 71 | 72 | Subject to the terms and conditions of this License, each Contributor hereby 73 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 74 | irrevocable (except as stated in this section) patent license to make, have 75 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 76 | such license applies only to those patent claims licensable by such Contributor 77 | that are necessarily infringed by their Contribution(s) alone or by combination 78 | of their Contribution(s) with the Work to which such Contribution(s) was 79 | submitted. If You institute patent litigation against any entity (including a 80 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 81 | Contribution incorporated within the Work constitutes direct or contributory 82 | patent infringement, then any patent licenses granted to You under this License 83 | for that Work shall terminate as of the date such litigation is filed. 84 | 85 | 4. Redistribution. 86 | 87 | You may reproduce and distribute copies of the Work or Derivative Works thereof 88 | in any medium, with or without modifications, and in Source or Object form, 89 | provided that You meet the following conditions: 90 | 91 | You must give any other recipients of the Work or Derivative Works a copy of 92 | this License; and 93 | You must cause any modified files to carry prominent notices stating that You 94 | changed the files; and 95 | You must retain, in the Source form of any Derivative Works that You distribute, 96 | all copyright, patent, trademark, and attribution notices from the Source form 97 | of the Work, excluding those notices that do not pertain to any part of the 98 | Derivative Works; and 99 | If the Work includes a "NOTICE" text file as part of its distribution, then any 100 | Derivative Works that You distribute must include a readable copy of the 101 | attribution notices contained within such NOTICE file, excluding those notices 102 | that do not pertain to any part of the Derivative Works, in at least one of the 103 | following places: within a NOTICE text file distributed as part of the 104 | Derivative Works; within the Source form or documentation, if provided along 105 | with the Derivative Works; or, within a display generated by the Derivative 106 | Works, if and wherever such third-party notices normally appear. The contents of 107 | the NOTICE file are for informational purposes only and do not modify the 108 | License. You may add Your own attribution notices within Derivative Works that 109 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 110 | provided that such additional attribution notices cannot be construed as 111 | modifying the License. 112 | You may add Your own copyright statement to Your modifications and may provide 113 | additional or different license terms and conditions for use, reproduction, or 114 | distribution of Your modifications, or for any such Derivative Works as a whole, 115 | provided Your use, reproduction, and distribution of the Work otherwise complies 116 | with the conditions stated in this License. 117 | 118 | 5. Submission of Contributions. 119 | 120 | Unless You explicitly state otherwise, any Contribution intentionally submitted 121 | for inclusion in the Work by You to the Licensor shall be under the terms and 122 | conditions of this License, without any additional terms or conditions. 123 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 124 | any separate license agreement you may have executed with Licensor regarding 125 | such Contributions. 126 | 127 | 6. Trademarks. 128 | 129 | This License does not grant permission to use the trade names, trademarks, 130 | service marks, or product names of the Licensor, except as required for 131 | reasonable and customary use in describing the origin of the Work and 132 | reproducing the content of the NOTICE file. 133 | 134 | 7. Disclaimer of Warranty. 135 | 136 | Unless required by applicable law or agreed to in writing, Licensor provides the 137 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 138 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 139 | including, without limitation, any warranties or conditions of TITLE, 140 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 141 | solely responsible for determining the appropriateness of using or 142 | redistributing the Work and assume any risks associated with Your exercise of 143 | permissions under this License. 144 | 145 | 8. Limitation of Liability. 146 | 147 | In no event and under no legal theory, whether in tort (including negligence), 148 | contract, or otherwise, unless required by applicable law (such as deliberate 149 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 150 | liable to You for damages, including any direct, indirect, special, incidental, 151 | or consequential damages of any character arising as a result of this License or 152 | out of the use or inability to use the Work (including but not limited to 153 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 154 | any and all other commercial damages or losses), even if such Contributor has 155 | been advised of the possibility of such damages. 156 | 157 | 9. Accepting Warranty or Additional Liability. 158 | 159 | While redistributing the Work or Derivative Works thereof, You may choose to 160 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 161 | other liability obligations and/or rights consistent with this License. However, 162 | in accepting such obligations, You may act only on Your own behalf and on Your 163 | sole responsibility, not on behalf of any other Contributor, and only if You 164 | agree to indemnify, defend, and hold each Contributor harmless for any liability 165 | incurred by, or claims asserted against, such Contributor by reason of your 166 | accepting any such warranty or additional liability. 167 | 168 | END OF TERMS AND CONDITIONS 169 | -------------------------------------------------------------------------------- /compactmap/src/main/resources/META-INF/NOTICE: -------------------------------------------------------------------------------- 1 | CompactMap, Copyright 2011, Vladimir Sitnikov 2 | -------------------------------------------------------------------------------- /compactmap/src/test/java/vlsi/utils/CompactHashMapClassTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | 23 | import java.io.*; 24 | import java.util.HashMap; 25 | import java.util.Iterator; 26 | import java.util.Map; 27 | 28 | public class CompactHashMapClassTest { 29 | @Before 30 | public void clearDefaults() { 31 | CompactHashMapDefaultValues.clear(); 32 | } 33 | 34 | @Test 35 | public void emptyMapReturnsNull() { 36 | CompactHashMap map = new CompactHashMap(); 37 | Assert.assertEquals(map.get("test"), null); 38 | Assert.assertEquals(map.size(), 0); 39 | } 40 | 41 | @Test 42 | public void nonDefaultIsStored() { 43 | CompactHashMap map = new CompactHashMap(); 44 | Assert.assertEquals(map.put("test", "abc"), null); 45 | Assert.assertEquals(map.get("test"), "abc"); 46 | Assert.assertEquals(map.size(), 1); 47 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 48 | } 49 | 50 | @Test 51 | public void putPutGet20() { 52 | CompactHashMap map = new CompactHashMap(); 53 | for (int i = 0; i < 20; i++) { 54 | Assert.assertEquals(map.put("k" + i, "v" + i), null); 55 | Assert.assertEquals(map.size(), i + 1); 56 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 57 | for (int j = 0; j < i; j++) { 58 | Assert.assertEquals(map.get("k" + j), "v" + j); 59 | } 60 | } 61 | } 62 | 63 | @Test 64 | public void putPutGet20WithDefault() { 65 | CompactHashMapDefaultValues.add("k1", "v1"); 66 | CompactHashMap map = new CompactHashMap(); 67 | int SIZE = 20; 68 | for (int i = 0; i < SIZE; i++) { 69 | Assert.assertEquals(map.put("k" + i, "v" + i), null); 70 | Assert.assertEquals(map.size(), i + 1); 71 | Assert.assertEquals(map.klass.getDefaultValues().size(), i >= 1 ? 1 : 0); 72 | for (int j = 0; j < i; j++) { 73 | Assert.assertEquals(map.get("k" + j), "v" + j); 74 | } 75 | } 76 | Assert.assertEquals(map.remove("k1"), "v1"); 77 | Assert.assertEquals(map.size(), SIZE - 1); 78 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 79 | } 80 | 81 | @Test 82 | public void putDefault() { 83 | CompactHashMapDefaultValues.add("default"); // all values are default 84 | 85 | CompactHashMap map = new CompactHashMap(); 86 | Assert.assertEquals(map.put("default", "a"), null); 87 | Assert.assertEquals(map.get("default"), "a"); 88 | Assert.assertEquals(map.size(), 1); 89 | Assert.assertEquals(map.klass.getDefaultValues().size(), 1); 90 | 91 | Assert.assertEquals(map.put("default", "b"), "a"); 92 | Assert.assertEquals(map.get("default"), "b"); 93 | Assert.assertEquals(map.size(), 1); 94 | Assert.assertEquals(map.klass.getDefaultValues().size(), 1); 95 | 96 | Assert.assertEquals(map.putOrRemove("default", CompactHashMapClass.REMOVED_OBJECT), "b"); 97 | Assert.assertEquals(map.get("default"), null); 98 | Assert.assertEquals(map.size(), 0); 99 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 100 | } 101 | 102 | @Test 103 | public void putDefaultSpecificValue() { 104 | CompactHashMapDefaultValues.add("test", "testDefaultValue"); 105 | CompactHashMap map = new CompactHashMap(); 106 | Assert.assertEquals(map.put("test", "testDefaultValue"), null); 107 | Assert.assertEquals(map.get("test"), "testDefaultValue"); 108 | Assert.assertEquals(map.size(), 1); 109 | Assert.assertEquals(map.klass.getDefaultValues().size(), 1); 110 | 111 | Assert.assertEquals(map.put("test", "non-default"), "testDefaultValue"); 112 | Assert.assertEquals(map.get("test"), "non-default"); 113 | Assert.assertEquals(map.size(), 1); 114 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 115 | 116 | Assert.assertEquals(map.put("test", "testDefaultValue"), "non-default"); 117 | Assert.assertEquals(map.get("test"), "testDefaultValue"); 118 | Assert.assertEquals(map.size(), 1); 119 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 120 | 121 | Assert.assertEquals(map.putOrRemove("test", CompactHashMapClass.REMOVED_OBJECT), "testDefaultValue"); 122 | Assert.assertEquals(map.get("test"), null); 123 | Assert.assertTrue(map.size() == 0 || map.size() == 1); // we allow to count deleted objects in size 124 | Assert.assertEquals(map.klass.getDefaultValues().size(), 0); 125 | } 126 | 127 | @Test 128 | public void putDefaultAndRegular() { 129 | CompactHashMapDefaultValues.add("default"); 130 | CompactHashMap map = new CompactHashMap(); 131 | 132 | Assert.assertEquals(map.put("default", "x"), null); 133 | Assert.assertEquals(map.get("default"), "x"); 134 | Assert.assertEquals(map.get("qwer"), null); 135 | Assert.assertEquals(map.size(), 1); 136 | Assert.assertEquals(map.klass.getDefaultValues().size(), 1); 137 | 138 | Assert.assertEquals(map.put("qwer", "b"), null); 139 | Assert.assertEquals(map.get("default"), "x"); 140 | Assert.assertEquals(map.get("qwer"), "b"); 141 | Assert.assertEquals(map.size(), 2); 142 | Assert.assertEquals(map.klass.getDefaultValues().size(), 1); 143 | 144 | Assert.assertEquals(map.put("default", "y"), "x"); 145 | Assert.assertEquals(map.get("default"), "y"); 146 | Assert.assertEquals(map.get("qwer"), "b"); 147 | Assert.assertEquals(map.size(), 2); 148 | Assert.assertEquals(map.klass.getDefaultValues().size(), 1); 149 | } 150 | 151 | @Test 152 | public void containsKeyOnNonExistingKey() { 153 | CompactHashMapDefaultValues.add("k1", "v1"); 154 | CompactHashMap map = new CompactHashMap(); 155 | Assert.assertFalse(map.containsKey("abcd")); 156 | Assert.assertFalse(map.containsKey("k1")); 157 | } 158 | 159 | @Test 160 | public void removeShouldBeReflectedInSize() { 161 | CompactHashMap map = new CompactHashMap(); 162 | map.put("charset", "UTF-8"); 163 | map.remove("charset"); 164 | Assert.assertEquals(map.size(), 0); 165 | Assert.assertFalse(map.containsKey("charset")); 166 | } 167 | 168 | @Test 169 | public void cmpactHashMapInstanceShouldBeSerializable() throws IOException, ClassNotFoundException { 170 | CompactHashMap map = new CompactHashMap(); 171 | map.put("charset", "UTF-8"); 172 | map.remove("charset"); 173 | CompactHashMap deserialized = serialize(map); 174 | Assert.assertEquals(deserialized.size(), 0); 175 | Assert.assertFalse(deserialized.containsKey("charset")); 176 | } 177 | 178 | private CompactHashMap serialize(CompactHashMap map) throws IOException, ClassNotFoundException { 179 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 180 | ObjectOutputStream oos = new ObjectOutputStream(baos); 181 | oos.writeObject(map); 182 | oos.close(); 183 | ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); 184 | return (CompactHashMap) ois.readObject(); 185 | } 186 | 187 | @Test 188 | public void defaultValuesAreDeserialized() throws IOException, ClassNotFoundException { 189 | CompactHashMapDefaultValues.add("k1", "v1"); 190 | CompactHashMapDefaultValues.add("k2", "v2"); 191 | CompactHashMap map = new CompactHashMap(); 192 | map.put("k1", "v1"); 193 | map.put("k2", "v2"); 194 | 195 | CompactHashMap deserialized = serialize(map); 196 | Assert.assertEquals(deserialized.size(), 2); 197 | Assert.assertEquals(deserialized.get("k1"), "v1"); 198 | Assert.assertEquals(deserialized.get("k2"), "v2"); 199 | } 200 | 201 | @Test 202 | public void defaultAndRegularValuesAreDeserialized() throws IOException, ClassNotFoundException { 203 | CompactHashMapDefaultValues.add("k1", "v1"); 204 | CompactHashMap map = new CompactHashMap(); 205 | map.put("k1", "v1"); 206 | map.put("k2", "v2"); 207 | 208 | CompactHashMap deserialized = serialize(map); 209 | Assert.assertEquals(deserialized.size(), 2); 210 | Assert.assertEquals(deserialized.get("k1"), "v1"); 211 | Assert.assertEquals(deserialized.get("k2"), "v2"); 212 | } 213 | 214 | @Test 215 | public void putNullWorks() { 216 | CompactHashMap map = new CompactHashMap(); 217 | map.put("k1", null); 218 | Assert.assertEquals(map.size(), 1); 219 | Assert.assertEquals(map.get("k1"), null); 220 | } 221 | 222 | @Test 223 | public void deleteNullWorks() { 224 | CompactHashMap map = new CompactHashMap(); 225 | map.put("k1", null); 226 | map.remove("k1"); 227 | Assert.assertEquals(map.size(), 0); 228 | Assert.assertEquals(map.get("k1"), null); 229 | } 230 | 231 | @Test 232 | public void deleteFromEntrySet() { 233 | CompactHashMap map = new CompactHashMap(); 234 | map.put("k1", "v1"); 235 | Assert.assertTrue("entry was removed", map.entrySet().remove(new Map.Entry() { 236 | public Object getKey() { 237 | return "k1"; 238 | } 239 | 240 | public Object getValue() { 241 | return "v1"; 242 | } 243 | 244 | public Object setValue(Object value) { 245 | return null; 246 | } 247 | })); 248 | Assert.assertEquals(map.size(), 0); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /compactmap/src/test/java/vlsi/utils/MapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package vlsi.utils; 18 | 19 | import com.google.common.collect.testing.MapTestSuiteBuilder; 20 | import com.google.common.collect.testing.TestStringMapGenerator; 21 | import com.google.common.collect.testing.features.CollectionFeature; 22 | import com.google.common.collect.testing.features.CollectionSize; 23 | import com.google.common.collect.testing.features.MapFeature; 24 | import junit.framework.Test; 25 | import junit.framework.TestSuite; 26 | 27 | import java.util.Map; 28 | 29 | public class MapTest { 30 | 31 | public static Test suite() { 32 | TestSuite suite = new TestSuite("All tests"); 33 | suite.addTest(tests("Simple", new TestStringMapGenerator() { 34 | @Override 35 | protected Map create(Map.Entry[] entries) { 36 | return populate(new CompactHashMap(), entries); 37 | } 38 | })); 39 | suite.addTest(tests("All as defaults", new TestStringMapGenerator() { 40 | @Override 41 | protected Map create(Map.Entry[] entries) { 42 | for (Map.Entry entry : entries) { 43 | CompactHashMapDefaultValues.add(entry.getKey(), entry.getValue()); 44 | } 45 | return populate(new CompactHashMap(), entries); 46 | } 47 | })); 48 | suite.addTest(tests("First as defaults", new TestStringMapGenerator() { 49 | @Override 50 | protected Map create(Map.Entry[] entries) { 51 | for (Map.Entry entry : entries) { 52 | CompactHashMapDefaultValues.add(entry.getKey(), entry.getValue()); 53 | break; 54 | } 55 | return populate(new CompactHashMap(), entries); 56 | } 57 | })); 58 | suite.addTest(tests("Keys as defaults", new TestStringMapGenerator() { 59 | @Override 60 | protected Map create(Map.Entry[] entries) { 61 | for (Map.Entry entry : entries) { 62 | CompactHashMapDefaultValues.add(entry.getKey()); 63 | } 64 | return populate(new CompactHashMap(), entries); 65 | } 66 | })); 67 | return suite; 68 | } 69 | 70 | private static TestSuite tests(final String name, TestStringMapGenerator generator) { 71 | return MapTestSuiteBuilder 72 | .using(generator) 73 | .named(name) 74 | .withTearDown("Simple".equals(name) ? null : new Runnable() { 75 | public void run() { 76 | CompactHashMapDefaultValues.clear(); 77 | } 78 | }) 79 | .withFeatures( 80 | MapFeature.GENERAL_PURPOSE, 81 | MapFeature.ALLOWS_NULL_KEYS, 82 | MapFeature.ALLOWS_NULL_VALUES, 83 | MapFeature.ALLOWS_ANY_NULL_QUERIES, 84 | MapFeature.RESTRICTS_KEYS, 85 | MapFeature.RESTRICTS_VALUES, 86 | CollectionFeature.SUPPORTS_ITERATOR_REMOVE, 87 | CollectionFeature.SERIALIZABLE, 88 | CollectionSize.ANY) 89 | .createTestSuite(); 90 | } 91 | 92 | private static > M populate(M map, Map.Entry[] entries) { 93 | for (Map.Entry entry : entries) { 94 | map.put(entry.getKey(), entry.getValue()); 95 | } 96 | return map; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /jmh/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | parent 4 | com.github.vlsi.compactmap 5 | 2.0.2-SNAPSHOT 6 | 7 | 4.0.0 8 | 9 | jmh 10 | jar 11 | 12 | Auto-generated JMH benchmark 13 | 14 | 15 | 3.0 16 | 17 | 18 | 19 | 20 | com.github.vlsi.compactmap 21 | compactmap 22 | 23 | 24 | org.openjdk.jmh 25 | jmh-core 26 | ${jmh.version} 27 | 28 | 29 | org.openjdk.jmh 30 | jmh-generator-annprocess 31 | ${jmh.version} 32 | provided 33 | 34 | 35 | org.pcollections 36 | pcollections 37 | compile 38 | 39 | 40 | com.github.krukow 41 | clj-ds 42 | compile 43 | 44 | 45 | 46 | 47 | UTF-8 48 | 1.21 49 | 1.6 50 | benchmarks 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-compiler-plugin 58 | 3.1 59 | 60 | ${javac.target} 61 | ${javac.target} 62 | ${javac.target} 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-shade-plugin 68 | 2.2 69 | 70 | 71 | package 72 | 73 | shade 74 | 75 | 76 | ${uberjar.name} 77 | 78 | 79 | org.openjdk.jmh.Main 80 | 81 | 82 | 83 | 84 | 88 | *:* 89 | 90 | META-INF/*.SF 91 | META-INF/*.DSA 92 | META-INF/*.RSA 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | maven-clean-plugin 105 | 2.5 106 | 107 | 108 | maven-deploy-plugin 109 | 2.8.1 110 | 111 | 112 | maven-install-plugin 113 | 2.5.1 114 | 115 | 116 | maven-jar-plugin 117 | 2.4 118 | 119 | 120 | maven-javadoc-plugin 121 | 2.9.1 122 | 123 | 124 | maven-resources-plugin 125 | 2.6 126 | 127 | 128 | maven-site-plugin 129 | 3.3 130 | 131 | 132 | maven-source-plugin 133 | 2.2.1 134 | 135 | 136 | maven-surefire-plugin 137 | 2.17 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /jmh/src/main/java/com/github/vlsi/compactmap/MyBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Vladimir Sitnikov 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.vlsi.compactmap; 18 | 19 | import com.github.andrewoma.dexx.collection.DerivedKeyHashMap; 20 | import com.github.andrewoma.dexx.collection.KeyFunction; 21 | import com.github.krukow.clj_ds.PersistentMap; 22 | import com.github.krukow.clj_lang.PersistentHashMap; 23 | import com.github.krukow.clj_lang.PersistentTreeMap; 24 | import org.openjdk.jmh.annotations.*; 25 | import org.openjdk.jmh.infra.Blackhole; 26 | import org.pcollections.HashTreePMap; 27 | import org.pcollections.PMap; 28 | 29 | import java.util.*; 30 | import java.util.concurrent.TimeUnit; 31 | 32 | @State(Scope.Thread) 33 | @BenchmarkMode(Mode.AverageTime) 34 | @OutputTimeUnit(TimeUnit.NANOSECONDS) 35 | public class MyBenchmark { 36 | @Param({"5", "10", "25", "50", "75", "100", "150", "500", "1000"}) 37 | int n = 100; 38 | List data; 39 | 40 | Map hashMap; 41 | Map treeMap; 42 | PMap pcollHashMap; 43 | com.github.andrewoma.dexx.collection.Map dexxTreeMap; 44 | com.github.andrewoma.dexx.collection.Map dexxSmartMap; 45 | com.github.andrewoma.dexx.collection.Map hashDexx; 46 | com.github.andrewoma.dexx.collection.Map hashDexxSmart; 47 | PersistentMap cljHashMap; 48 | PersistentMap cljTreeMap; 49 | 50 | public static class Value { 51 | int key, value; 52 | 53 | public Value(int key, int value) { 54 | this.key = key; 55 | this.value = value; 56 | } 57 | } 58 | 59 | @Setup 60 | public void init() { 61 | data = new ArrayList(n); 62 | for (int i = 0; i < n; i++) { 63 | data.add(new Value(i * 1001, i)); 64 | } 65 | Collections.shuffle(data); 66 | 67 | pcollHashMap = HashTreePMap.empty(); 68 | dexxTreeMap = new com.github.andrewoma.dexx.collection.TreeMap(); 69 | KeyFunction keyFunction = new KeyFunction() { 70 | public Integer key(Value value) { 71 | return value.key; 72 | } 73 | }; 74 | dexxSmartMap = 75 | new com.github.andrewoma.dexx.collection.TreeMap(null, keyFunction); 76 | hashDexx = com.github.andrewoma.dexx.collection.HashMap.empty(); 77 | hashDexxSmart = new DerivedKeyHashMap(keyFunction); 78 | cljHashMap = PersistentHashMap.emptyMap(); 79 | cljTreeMap = PersistentTreeMap.EMPTY; 80 | hashMap = new HashMap(); 81 | treeMap = new TreeMap(); 82 | for (Value value : data) { 83 | pcollHashMap = pcollHashMap.plus(value.key, value.value); 84 | dexxTreeMap = dexxTreeMap.put(value.key, value.value); 85 | dexxSmartMap = dexxSmartMap.put(value.key, value); 86 | hashDexx = hashDexx.put(value.key, value); 87 | hashDexxSmart = hashDexxSmart.put(value.key, value); 88 | cljHashMap = cljHashMap.plus(value.key, value.value); 89 | cljTreeMap = cljTreeMap.plus(value.key, value.value); 90 | hashMap.put(value.key, value.value); 91 | treeMap.put(value.key, value.value); 92 | } 93 | } 94 | 95 | @Benchmark 96 | public void hashPcoll(Blackhole b) { 97 | List data = this.data; 98 | for (int i = 0; i < data.size(); i++) { 99 | Value value = data.get(i); 100 | b.consume(pcollHashMap.get(value.key)); 101 | } 102 | } 103 | 104 | @Benchmark 105 | public void treeDexx(Blackhole b) { 106 | List data = this.data; 107 | for (int i = 0; i < data.size(); i++) { 108 | Value value = data.get(i); 109 | b.consume(dexxTreeMap.get(value.key)); 110 | } 111 | } 112 | 113 | @Benchmark 114 | public void treeDexxSmart(Blackhole b) { 115 | List data = this.data; 116 | for (int i = 0; i < data.size(); i++) { 117 | Value value = data.get(i); 118 | b.consume(dexxSmartMap.get(value.key)); 119 | } 120 | } 121 | 122 | @Benchmark 123 | public void hashDexx(Blackhole b) { 124 | List data = this.data; 125 | for (int i = 0; i < data.size(); i++) { 126 | Value value = data.get(i); 127 | b.consume(hashDexx.get(value.key)); 128 | } 129 | } 130 | 131 | @Benchmark 132 | public void hashDexxSmart(Blackhole b) { 133 | List data = this.data; 134 | for (int i = 0; i < data.size(); i++) { 135 | Value value = data.get(i); 136 | b.consume(hashDexxSmart.get(value.key)); 137 | } 138 | } 139 | 140 | @Benchmark 141 | public void hashClj(Blackhole b) { 142 | List data = this.data; 143 | for (int i = 0; i < data.size(); i++) { 144 | Value value = data.get(i); 145 | b.consume(cljHashMap.get(value.key)); 146 | } 147 | } 148 | 149 | @Benchmark 150 | public void treeClj(Blackhole b) { 151 | List data = this.data; 152 | for (int i = 0; i < data.size(); i++) { 153 | Value value = data.get(i); 154 | b.consume(cljTreeMap.get(value.key)); 155 | } 156 | } 157 | 158 | @Benchmark 159 | public void hashBase(Blackhole b) { 160 | List data = this.data; 161 | for (int i = 0; i < data.size(); i++) { 162 | Value value = data.get(i); 163 | b.consume(hashMap.get(value.key)); 164 | } 165 | } 166 | 167 | @Benchmark 168 | public void treeBase(Blackhole b) { 169 | List data = this.data; 170 | for (int i = 0; i < data.size(); i++) { 171 | Value value = data.get(i); 172 | b.consume(treeMap.get(value.key)); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /jol/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | parent 5 | com.github.vlsi.compactmap 6 | 2.0.2-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | jol 11 | jar 12 | 13 | Compact HashMap layout/size tests 14 | 15 | 16 | 17 | com.github.vlsi.compactmap 18 | compactmap 19 | 20 | 21 | org.openjdk.jol 22 | jol-core 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter 27 | 28 | 29 | org.pcollections 30 | pcollections 31 | 32 | 33 | com.github.krukow 34 | clj-ds 35 | 36 | 37 | -------------------------------------------------------------------------------- /jol/src/test/java/CompactMapSizeTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test; 2 | import org.openjdk.jol.info.GraphLayout; 3 | import vlsi.utils.CompactHashMap; 4 | 5 | import java.lang.reflect.Field; 6 | import java.util.*; 7 | 8 | public class CompactMapSizeTest { 9 | private final static ArrayList keys = new ArrayList(); 10 | 11 | private final BitSet seenKeys = new BitSet(); 12 | 13 | static { 14 | for (int i = 0; i < 1000; i++) { 15 | keys.add("key" + i); 16 | } 17 | } 18 | 19 | private int fill(Map map, int size, Random rnd, boolean keySize, boolean valueSize) { 20 | int overhead = 0; 21 | seenKeys.clear(); 22 | for (int i = 0; i < size; i++) { 23 | int index = rnd.nextInt(keys.size()); 24 | if (seenKeys.get(index)) { 25 | i--; 26 | continue; 27 | } 28 | seenKeys.set(index); 29 | String key = keys.get(index); 30 | String value = "value" + index; 31 | map.put(key, value); 32 | if (keySize) 33 | overhead += GraphLayout.parseInstance(key).totalSize(); 34 | if (valueSize) 35 | overhead += GraphLayout.parseInstance(value).totalSize(); 36 | } 37 | return overhead; 38 | } 39 | 40 | @Test 41 | public void business() { 42 | ArrayList> 43 | data = new ArrayList>(); 44 | Random rnd = new Random(); 45 | for (int j = 0; j < 10; j++) { 46 | for (int i = 0; i < 100; i++) { 47 | rnd.setSeed(42 * j); 48 | Map m; 49 | if (false) { 50 | m = new CompactHashMap(); 51 | } else { 52 | m = new HashMap(); 53 | } 54 | fill(m, 100, rnd, false, false); 55 | data.add(m); 56 | } 57 | } 58 | System.out.println(GraphLayout.parseInstance(data).toFootprint()); 59 | } 60 | 61 | /** 62 | * Proof that {@link vlsi.utils.CompactHashMap} has ~4 bytes per entry overhead (i.e. the size of a reference). 63 | * {@link java.util.HashMap} has 36 bytes per entry 64 | * 65 | * @throws NoSuchFieldException 66 | * @throws IllegalAccessException 67 | */ 68 | @Test 69 | public void perEntryOverhead() throws NoSuchFieldException, IllegalAccessException { 70 | Random rnd = new Random(); 71 | Field klass = CompactHashMap.class.getDeclaredField("klass"); 72 | klass.setAccessible(true); 73 | 74 | System.out.println("number_of_keys;compactmap_estimate;compactmap_size;hashmap_estimate;hashmap_size"); 75 | for (int j = 0; j < 101; j++) { 76 | rnd.setSeed(42 * j); 77 | Map m = new CompactHashMap(); 78 | long compactValues = fill(m, j, rnd, false, true); 79 | 80 | rnd.setSeed(42 * j); 81 | Map hm = new HashMap(j, 1.0f); 82 | long hmVals = fill(hm, j, rnd, true, true) + (j == 0 ? 16 : 0); 83 | 84 | long compactTotalSize = GraphLayout.parseInstance(m).totalSize(); 85 | compactValues += GraphLayout.parseInstance(klass.get(m)).totalSize(); 86 | 87 | if (false) { 88 | System.out.println(GraphLayout.parseInstance(hm).toFootprint()); 89 | } 90 | 91 | long hashMapTotalSize = GraphLayout.parseInstance(hm).totalSize(); 92 | System.out.println(j 93 | + ";" + (32 + (j < 4 ? 0 : (16 + 4 * (j - 2)))) 94 | + ";" + (compactTotalSize - compactValues) 95 | + ";" + (48 + (j == 0 ? 0 : (16 + (4 + 32) * j))) 96 | + ";" + (hashMapTotalSize - hmVals) 97 | ); 98 | } 99 | } 100 | 101 | } 102 | 103 | -------------------------------------------------------------------------------- /jol/src/test/java/NoDefaultsTest.java: -------------------------------------------------------------------------------- 1 | import com.github.andrewoma.dexx.collection.KeyFunction; 2 | import com.github.krukow.clj_ds.PersistentMap; 3 | import com.github.krukow.clj_lang.PersistentHashMap; 4 | import com.github.krukow.clj_lang.PersistentTreeMap; 5 | import org.junit.jupiter.api.Disabled; 6 | import org.junit.jupiter.api.Test; 7 | import org.openjdk.jol.info.ClassLayout; 8 | import org.openjdk.jol.info.GraphLayout; 9 | import org.pcollections.HashTreePMap; 10 | import org.pcollections.PMap; 11 | import vlsi.utils.CompactHashMap; 12 | 13 | import java.util.*; 14 | 15 | public class NoDefaultsTest { 16 | @Test 17 | @Disabled 18 | public void emptyCompactMap() { 19 | Map m = new CompactHashMap(); 20 | System.out.println(ClassLayout.parseClass(m.getClass()).toPrintable(m)); 21 | System.out.println(GraphLayout.parseInstance(m).toPrintable()); 22 | } 23 | 24 | @Test 25 | @Disabled 26 | public void emptyHashMap() { 27 | Map m = new HashMap(); 28 | System.out.println(ClassLayout.parseClass(m.getClass()).toPrintable(m)); 29 | ; 30 | System.out.println(GraphLayout.parseInstance(m).toPrintable()); 31 | } 32 | 33 | public static class Value { 34 | Integer key; 35 | int value; 36 | 37 | public Value(Integer key, int value) { 38 | this.key = key; 39 | this.value = value; 40 | } 41 | } 42 | 43 | /** 44 | * Here memory footprint of persistent collections is compared. 45 | */ 46 | @Test 47 | public void simpleValues() { 48 | Map m = new CompactHashMap(); 49 | int n = 100; 50 | 51 | List data = new ArrayList(n); 52 | for (int i = 0; i < n; i++) { 53 | data.add(new Value(i * 10, i)); 54 | } 55 | Collections.shuffle(data); 56 | { 57 | ArrayList x = new ArrayList(); 58 | Map u = new HashMap(); 59 | x.add(u); 60 | for (Value value : data) { 61 | u = new HashMap(u); 62 | u.put(value.key, value.value); 63 | x.add(u); 64 | } 65 | GraphLayout gl = GraphLayout.parseInstance(x); 66 | System.out.print(gl.toFootprint()); 67 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 68 | } 69 | { 70 | ArrayList x = new ArrayList(); 71 | PMap u = HashTreePMap.empty(); 72 | x.add(u); 73 | for (Value value : data) { 74 | u = u.plus(value.key, value.value); 75 | x.add(u); 76 | } 77 | GraphLayout gl = GraphLayout.parseInstance(x); 78 | System.out.print(gl.toFootprint()); 79 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 80 | } 81 | { 82 | ArrayList x = new ArrayList(); 83 | com.github.andrewoma.dexx.collection.Map u = new com.github.andrewoma.dexx.collection.TreeMap(); 84 | x.add(u); 85 | for (Value value : data) { 86 | u = u.put(value.key, value.value); 87 | x.add(u); 88 | } 89 | GraphLayout gl = GraphLayout.parseInstance(x); 90 | System.out.print(gl.toFootprint()); 91 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 92 | } 93 | { 94 | ArrayList x = new ArrayList(); 95 | com.github.andrewoma.dexx.collection.Map u = 96 | new com.github.andrewoma.dexx.collection.TreeMap(null, new KeyFunction() { 97 | public Integer key(Value value) { 98 | return value.key; 99 | } 100 | }); 101 | x.add(u); 102 | for (Value value : data) { 103 | u = u.put(value.key, value); 104 | x.add(u); 105 | } 106 | GraphLayout gl = GraphLayout.parseInstance(x); 107 | System.out.print(gl.toFootprint()); 108 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 109 | } 110 | { 111 | ArrayList x = new ArrayList(); 112 | com.github.andrewoma.dexx.collection.Map u = new com.github.andrewoma.dexx.collection.HashMap(); 113 | x.add(u); 114 | for (Value value : data) { 115 | u = u.put(value.key, value.value); 116 | x.add(u); 117 | } 118 | GraphLayout gl = GraphLayout.parseInstance(x); 119 | System.out.print(gl.toFootprint()); 120 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 121 | } 122 | { 123 | ArrayList x = new ArrayList(); 124 | com.github.andrewoma.dexx.collection.Map u = 125 | new com.github.andrewoma.dexx.collection.DerivedKeyHashMap(new KeyFunction() { 126 | public Integer key(Value value) { 127 | return value.key; 128 | } 129 | }); 130 | x.add(u); 131 | for (Value value : data) { 132 | u = u.put(value.key, value); 133 | x.add(u); 134 | } 135 | GraphLayout gl = GraphLayout.parseInstance(x); 136 | System.out.print(gl.toFootprint()); 137 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 138 | } 139 | { 140 | ArrayList x = new ArrayList(); 141 | PersistentMap u = PersistentHashMap.emptyMap(); 142 | x.add(u); 143 | for (Value value : data) { 144 | u = u.plus(value.key, value.value); 145 | x.add(u); 146 | } 147 | GraphLayout gl = GraphLayout.parseInstance(x); 148 | System.out.print(gl.toFootprint()); 149 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 150 | } 151 | { 152 | ArrayList x = new ArrayList(); 153 | PersistentMap u = PersistentTreeMap.EMPTY; 154 | x.add(u); 155 | for (Value value : data) { 156 | u = u.plus(value.key, value.value); 157 | x.add(u); 158 | } 159 | GraphLayout gl = GraphLayout.parseInstance(x); 160 | System.out.print(gl.toFootprint()); 161 | System.out.println("gl.totalSize()*1.f/n = " + gl.totalSize() * 1.f / n + "\n"); 162 | } 163 | // System.out.println(ClassLayout.parseClass(m.getClass()).toPrintable(m));; 164 | // System.out.println(GraphLayout.parseInstance(m).toPrintable()); 165 | } 166 | /* 167 | i = 0; 272 168 | i = 1; 384 169 | i = 2; 504 170 | i = 3; 640 171 | i = 4; 736 172 | i = 5; 832 173 | i = 6; 928 174 | i = 7; 1048 175 | i = 8; 1176 176 | i = 9; 1272 177 | i = 10; 1376 178 | i = 11; 1480 179 | i = 12; 1584 180 | i = 13; 1736 181 | i = 14; 1840 182 | i = 15; 1944 183 | i = 16; 2112 184 | i = 17; 2216 185 | i = 18; 2320 186 | i = 19; 2424 187 | i = 20; 2528 188 | i = 21; 2632 189 | i = 22; 2736 190 | i = 23; 2840 191 | i = 24; 2944 192 | i = 25; 3144 193 | i = 26; 3248 194 | i = 27; 3352 195 | i = 28; 3456 196 | i = 29; 3560 197 | i = 30; 3664 198 | i = 31; 3768 199 | i = 32; 4000 200 | i = 33; 4104 201 | i = 34; 4208 202 | i = 35; 4312 203 | i = 36; 4416 204 | i = 37; 4520 205 | i = 38; 4624 206 | i = 39; 4728 207 | i = 40; 4832 208 | i = 41; 4936 209 | i = 42; 5040 210 | i = 43; 5144 211 | i = 44; 5248 212 | i = 45; 5352 213 | i = 46; 5456 214 | i = 47; 5560 215 | i = 48; 5664 216 | i = 49; 5960 217 | i = 50; 6064 218 | i = 51; 6168 219 | i = 52; 6272 220 | i = 53; 6376 221 | i = 54; 6480 222 | i = 55; 6584 223 | i = 56; 6688 224 | i = 57; 6792 225 | i = 58; 6896 226 | i = 59; 7000 227 | i = 60; 7104 228 | i = 61; 7208 229 | i = 62; 7312 230 | i = 63; 7416 231 | i = 64; 7776 232 | i = 65; 7880 233 | i = 66; 7984 234 | i = 67; 8088 235 | i = 68; 8192 236 | i = 69; 8296 237 | i = 70; 8400 238 | i = 71; 8504 239 | i = 72; 8608 240 | i = 73; 8712 241 | i = 74; 8816 242 | i = 75; 8920 243 | i = 76; 9024 244 | i = 77; 9128 245 | i = 78; 9232 246 | i = 79; 9336 247 | i = 80; 9440 248 | i = 81; 9544 249 | i = 82; 9648 250 | i = 83; 9752 251 | i = 84; 9856 252 | i = 85; 9960 253 | i = 86; 10064 254 | i = 87; 10168 255 | i = 88; 10272 256 | i = 89; 10376 257 | i = 90; 10480 258 | i = 91; 10584 259 | i = 92; 10688 260 | i = 93; 10792 261 | i = 94; 10896 262 | i = 95; 11000 263 | i = 96; 11104 264 | i = 97; 11592 265 | i = 98; 11696 266 | i = 99; 11800 267 | */ 268 | } 269 | 270 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.github.vlsi.compactmap 6 | parent 7 | pom 8 | 2.0.2-SNAPSHOT 9 | 10 | compactmap 11 | jol 12 | jmh 13 | 14 | 15 | 16 | UTF-8 17 | 1.5 18 | 19 | 20 | Compact HashMap parent 21 | Hash table implementation modelled after memory efficient V8's Fast Property Access 22 | 23 | 24 | Apache-2.0 25 | https://www.apache.org/licenses/LICENSE-2.0 26 | 27 | 28 | 29 | https://github.com/vlsi/compactmap 30 | 31 | https://github.com/vlsi/compactmap 32 | scm:git:https://github.com/vlsi/compactmap.git 33 | scm:git:https://github.com/vlsi/compactmap.git 34 | HEAD 35 | 36 | 37 | 38 | 39 | Vladimir Sitnikov 40 | sitnikov.vladimir@gmail.com 41 | 42 | 43 | 44 | 45 | 46 | ossrh 47 | https://oss.sonatype.org/content/repositories/snapshots/ 48 | 49 | 50 | ossrh 51 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 52 | 53 | 54 | 55 | 56 | 57 | 58 | com.github.vlsi.compactmap 59 | compactmap 60 | ${project.version} 61 | 62 | 63 | com.github.andrewoma.dexx 64 | dexx-collections 65 | 0.2 66 | 67 | 68 | org.pcollections 69 | pcollections 70 | 3.0.3 71 | test 72 | 73 | 74 | com.github.krukow 75 | clj-ds 76 | 0.0.4 77 | test 78 | 79 | 80 | org.openjdk.jol 81 | jol-core 82 | 0.9 83 | test 84 | 85 | 86 | org.junit.jupiter 87 | junit-jupiter 88 | 5.4.2 89 | test 90 | 91 | 92 | org.junit.vintage 93 | junit-vintage-engine 94 | 5.4.2 95 | test 96 | 97 | 98 | com.google.guava 99 | guava-testlib 100 | 27.1-jre 101 | test 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-compiler-plugin 112 | 113 | ${javac.target} 114 | ${javac.target} 115 | 116 | 117 | 118 | org.sonatype.plugins 119 | nexus-staging-maven-plugin 120 | 1.6.3 121 | true 122 | 123 | ossrh 124 | https://oss.sonatype.org/ 125 | true 126 | 127 | 128 | 129 | org.apache.maven.plugins 130 | maven-source-plugin 131 | 2.2.1 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-javadoc-plugin 136 | 2.9.1 137 | 138 | 139 | org.apache.maven.plugins 140 | maven-gpg-plugin 141 | 1.5 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-release-plugin 146 | 2.5 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | gpg-sign 155 | 156 | false 157 | 158 | gpg.passphrase 159 | 160 | 161 | 162 | 163 | 164 | org.apache.maven.plugins 165 | maven-gpg-plugin 166 | 167 | 168 | sign-artifacts 169 | verify 170 | 171 | sign 172 | 173 | 174 | 175 | 176 | 177 | org.sonatype.plugins 178 | nexus-staging-maven-plugin 179 | 180 | 181 | org.apache.maven.plugins 182 | maven-release-plugin 183 | 184 | true 185 | false 186 | release 187 | deploy nexus-staging:release 188 | 189 | 190 | 191 | 192 | 193 | 194 | release 195 | 196 | false 197 | 198 | 199 | 200 | 201 | org.apache.maven.plugins 202 | maven-source-plugin 203 | 204 | 205 | attach-sources 206 | 207 | jar-no-fork 208 | 209 | 210 | 211 | 212 | 213 | org.apache.maven.plugins 214 | maven-javadoc-plugin 215 | 216 | 217 | attach-javadocs 218 | 219 | jar 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | --------------------------------------------------------------------------------