001/* 002 * #%L 003 * Netarchivesuite - common 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023package dk.netarkivet.common.utils; 024 025import java.util.BitSet; 026import java.util.HashSet; 027import java.util.Set; 028 029/** 030 * A sparse implementation of a BitSet, that does not require memory linear to the largest index. This is done at the 031 * cost of performance, but should be fairly efficient on few set bits. 032 */ 033@SuppressWarnings({"serial"}) 034public class SparseBitSet extends BitSet { 035 036 /** A set of the indices of bits that are set in this BitSet. */ 037 private Set<Integer> setbits = new HashSet<Integer>(); 038 039 /** 040 * Initialise the bitset. 041 */ 042 public SparseBitSet() { 043 // Initialise super class to a zero-length bitset, to avoid allocating 044 // a bit array. 045 super(0); 046 } 047 048 @Override 049 public void flip(int bitIndex) { 050 if (bitIndex < 0) { 051 throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); 052 } 053 if (setbits.contains(bitIndex)) { 054 setbits.remove(bitIndex); 055 } else { 056 setbits.add(bitIndex); 057 } 058 } 059 060 @Override 061 public void flip(int fromIndex, int toIndex) { 062 if (fromIndex < 0) { 063 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 064 } 065 if (toIndex < 0) { 066 throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex); 067 } 068 if (fromIndex > toIndex) { 069 throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " > toIndex: " + toIndex); 070 } 071 for (int i = fromIndex; i < toIndex; i++) { 072 flip(i); 073 } 074 } 075 076 @Override 077 public void set(int bitIndex) { 078 if (bitIndex < 0) { 079 throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); 080 } 081 setbits.add(bitIndex); 082 } 083 084 @Override 085 public void set(int bitIndex, boolean value) { 086 if (bitIndex < 0) { 087 throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); 088 } 089 if (value) { 090 setbits.add(bitIndex); 091 } else { 092 setbits.remove(bitIndex); 093 } 094 } 095 096 @Override 097 public void set(int fromIndex, int toIndex) { 098 if (fromIndex < 0) { 099 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 100 } 101 if (toIndex < 0) { 102 throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex); 103 } 104 if (fromIndex > toIndex) { 105 throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " > toIndex: " + toIndex); 106 } 107 for (int i = fromIndex; i < toIndex; i++) { 108 set(i); 109 } 110 } 111 112 @Override 113 public void set(int fromIndex, int toIndex, boolean value) { 114 if (fromIndex < 0) { 115 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 116 } 117 if (toIndex < 0) { 118 throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex); 119 } 120 if (fromIndex > toIndex) { 121 throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " > toIndex: " + toIndex); 122 } 123 for (int i = fromIndex; i < toIndex; i++) { 124 set(i, value); 125 } 126 } 127 128 @Override 129 public void clear(int bitIndex) { 130 if (bitIndex < 0) { 131 throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); 132 } 133 setbits.remove(bitIndex); 134 } 135 136 @Override 137 public void clear(int fromIndex, int toIndex) { 138 if (fromIndex < 0) { 139 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 140 } 141 if (toIndex < 0) { 142 throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex); 143 } 144 if (fromIndex > toIndex) { 145 throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " > toIndex: " + toIndex); 146 } 147 for (int i = fromIndex; i < toIndex; i++) { 148 clear(i); 149 } 150 } 151 152 @Override 153 public void clear() { 154 setbits.clear(); 155 } 156 157 @Override 158 public boolean get(int bitIndex) { 159 if (bitIndex < 0) { 160 throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); 161 } 162 return setbits.contains(bitIndex); 163 } 164 165 @Override 166 public BitSet get(int fromIndex, int toIndex) { 167 if (fromIndex < 0) { 168 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 169 } 170 if (toIndex < 0) { 171 throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex); 172 } 173 if (fromIndex > toIndex) { 174 throw new IndexOutOfBoundsException("fromIndex: " + fromIndex + " > toIndex: " + toIndex); 175 } 176 SparseBitSet bitsubset = new SparseBitSet(); 177 for (int i : setbits) { 178 if (i >= fromIndex && i < toIndex) { 179 bitsubset.set(i - fromIndex); 180 } 181 } 182 return bitsubset; 183 } 184 185 @Override 186 public int nextSetBit(int fromIndex) { 187 if (fromIndex < 0) { 188 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 189 } 190 int index = -1; 191 for (Integer i : setbits) { 192 if (i >= fromIndex && (index == -1 || i < index)) { 193 index = i; 194 } 195 } 196 return index; 197 } 198 199 @Override 200 public int nextClearBit(int fromIndex) { 201 if (fromIndex < 0) { 202 throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex); 203 } 204 for (int i = fromIndex; i > 0; i++) { 205 if (!get(i)) { 206 return i; 207 } 208 } 209 return Integer.MIN_VALUE; 210 } 211 212 @Override 213 public int length() { 214 int index = -1; 215 for (Integer i : setbits) { 216 if (i > index) { 217 index = i; 218 } 219 } 220 return index + 1; 221 } 222 223 @Override 224 public boolean isEmpty() { 225 return setbits.isEmpty(); 226 } 227 228 @Override 229 public boolean intersects(BitSet set) { 230 for (Integer index : setbits) { 231 if (set.get(index)) { 232 return true; 233 } 234 } 235 return false; 236 } 237 238 @Override 239 public int cardinality() { 240 return setbits.size(); 241 } 242 243 @Override 244 public void and(BitSet set) { 245 Set<Integer> andbits = new HashSet<Integer>(); 246 for (Integer index : setbits) { 247 if (set.get(index)) { 248 andbits.add(index); 249 } 250 } 251 setbits = andbits; 252 } 253 254 @Override 255 public void or(BitSet set) { 256 Set<Integer> orbits = new HashSet<Integer>(setbits); 257 for (int index = set.nextSetBit(0); index != -1; index = set.nextSetBit(index + 1)) { 258 orbits.add(index); 259 } 260 setbits = orbits; 261 } 262 263 @Override 264 public void xor(BitSet set) { 265 Set<Integer> xorbits = new HashSet<Integer>(); 266 for (Integer index : setbits) { 267 if (!set.get(index)) { 268 xorbits.add(index); 269 } 270 } 271 for (int index = set.nextSetBit(0); index != -1; index = set.nextSetBit(index + 1)) { 272 if (!setbits.contains(index)) { 273 xorbits.add(index); 274 } 275 } 276 setbits = xorbits; 277 } 278 279 @Override 280 public void andNot(BitSet set) { 281 Set<Integer> andnotbits = new HashSet<Integer>(setbits); 282 for (Integer index : setbits) { 283 if (set.get(index)) { 284 andnotbits.remove(index); 285 } 286 } 287 setbits = andnotbits; 288 } 289 290 /** 291 * A hash code for this bit set. Note: The hash codes are not implemented to be compatible with 292 * java.util.BitSet#hashCode(). Implementing that algorithm would be difficult and inefficient on the current 293 * implementation. 294 * 295 * @return A hashcode. 296 */ 297 public int hashCode() { 298 return setbits.hashCode(); 299 } 300 301 /** 302 * In contrast with {@link BitSet#size()} this does not return the size in bytes used to represent this set. 303 * Instead, it returns the same as {@link #length()} for compatibility with {@link BitSet}. The actual space used is 304 * a hashset of size {@link #cardinality()}. 305 * 306 * @return the same as {@link #length()} 307 */ 308 public int size() { 309 return length(); 310 } 311 312 /** 313 * Two SparseBitSets are considered equal if they contain the same bits. 314 * <p> 315 * Note: A SparseBitSet is never considered equal to a BitSet. This would be impossible to implement in a way so 316 * equality is symmetric, since {@link BitSet#equals(Object)} is implemented using its private fields to determine 317 * equality. 318 * 319 * @param obj The object to compare for equality. 320 * @return true, if obj is a SparseBitSet and contains the same bits as this object. 321 */ 322 public boolean equals(Object obj) { 323 return obj instanceof SparseBitSet && setbits.equals(((SparseBitSet) obj).setbits); 324 } 325 326 @Override 327 public Object clone() { 328 super.clone(); 329 SparseBitSet newSparseBitSet = new SparseBitSet(); 330 newSparseBitSet.setbits = new HashSet<Integer>(setbits); 331 return newSparseBitSet; 332 } 333 334 @Override 335 public String toString() { 336 return setbits.toString(); 337 } 338 339}