/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.github.ggrandes.kvstore.structures.set; import java.util.Arrays; import org.apache.log4j.Logger; /** * Native Int SortedArray * This class is NOT Thread-Safe * * @author Guillermo Grandes / guillermo.grandes[at]gmail.com */ public class SortedIntArraySet { public static final int NULL_VALUE = Integer.MIN_VALUE; private static final Logger log = Logger.getLogger(SortedIntArraySet.class); // public int[] keys; //注意allocated的值和数组的索引的区别. 假设数组有5个元素, 则allocated=5, 数组最大的索引是4 public int allocated = 0; //已经分配的数量 /** * Create with initial size * @param size */ public SortedIntArraySet(final int size) { allocArray(size); } /** * Alloc array * @param size */ private final void allocArray(final int size) { keys = new int[size]; } /** * Resize array */ private final void resizeArray() { if (log.isDebugEnabled()) log.debug("resizeArray size=" + keys.length + " newsize=" + (keys.length << 1)); final int[] newkeys = new int[keys.length << 1]; // double space //allocated表示数组的长度. 也就是arraycopy需要从源数组中复制的长度 System.arraycopy(keys, 0, newkeys, 0, allocated); keys = newkeys; } /** * Find slot by key 根据key寻找这个key将要存放的slot位置. * 因为Set是要有序的. 所以使用二分查找. * @param searchKey 要查找的键 * @return 键在有序集合中的位置 */ private final int findSlotByKey(final int searchKey) { //二分查找的from=0, end是数组的长度array.length, 而不是最后一个元素的index return Arrays.binarySearch(keys, 0, allocated, searchKey); } /** * Is empty? 初始时allocated=0, 表示没有分配任何元素 * @return */ public boolean isEmpty() { // empty return (allocated <= 0); } /** * Is full? * @return */ private final boolean isFull() { // full if (log.isDebugEnabled()) log.debug("allocated=" + allocated + " keys.length=" + keys.length); return (allocated >= keys.length); } /** * Clear all elements */ public void clear() { Arrays.fill(keys, NULL_VALUE); allocated = 0; } /** * insert element 插入一个元素 * 往指定的位置插入一个元素, 这个指定位置的元素以及之后的元素都要后移一位 * * 0 1 3 5 7 9 * ^2 * findSlotByKey=2, srcPos=2 * * Coppy(eles1, 2, eles1, 3, (allocated-srcPos)) allocated=6,srcPos=2,需要一定6-2=4位 * eles1: 0 1 3 5 7 9 从eles1的第2个位置开始,复制到eles1的第3个位置开始,一共复制4位 * eles1: 0 1 3 3 5 7 9 * * move完后,要将srcPos的位置设置为要插入的元素. */ private final void moveElementsRight(final int[] elements, final int srcPos) { if (log.isDebugEnabled()) log.debug("moveElementsRight(" + srcPos + ") allocated=" + allocated + ":" + keys.length + ":" + (allocated - srcPos) + ":" + (keys.length - srcPos - 1)); System.arraycopy(elements, srcPos, elements, srcPos + 1, (allocated - srcPos)); } /** * remove element 删除一个元素 * 0 1 2 3 5 7 9 * × * findSlotByKey=2, srcPos=2, * 从当前要删除的位置的下一个位置开始,复制(allocated - srcPos - 1), 复制后的位置的开始位置是srcPos */ private final void moveElementsLeft(final int[] elements, final int srcPos) { if (log.isDebugEnabled()) log.debug("moveElementsLeft(" + srcPos + ") allocated=" + allocated + ":" + keys.length + ":" + (allocated - srcPos - 1) + ":" + (keys.length - srcPos - 1)); System.arraycopy(elements, srcPos + 1, elements, srcPos, (allocated - srcPos - 1)); } /** * remove key * @param key * @return */ public boolean remove(final int key) { int slot = findSlotByKey(key); if (slot >= 0) { return removeSlot(slot); } //如果slot<0, 表示没有找到key,就无法删除了! return false; } /** * remove slot * @param slot 实际上是数组的索引 * @return */ private final boolean removeSlot(final int slot) { if (slot < 0) { log.error("faking slot=" + slot + " allocated=" + allocated); return false; } if (slot < allocated) { moveElementsLeft(keys, slot); } //数组的容量减1 if (allocated > 0) allocated--; if (log.isDebugEnabled()) log.debug("erased up key=" + keys[allocated]); //现在allocated实际上指向还没删除前的最后一个元素的索引. 因为allocated--, 即allocated=array.length-1 //将要删除的元素设置为null keys[allocated] = NULL_VALUE; return true; } /** * put key * @param key * @return */ public boolean put(final int key) { if (isFull()) { // full resizeArray(); } int slot = findSlotByKey(key); //二分查找,找到key的话,slot>0 if (slot >= 0) { if (log.isDebugEnabled()) log.debug("key already exists: " + key); return false; // key already exist } //如果没有找到key的话,返回的是一个负数 slot = ((-slot) - 1); return addSlot(slot, key); } /** * add slot 往指定的位置添加一个key * @param slot * @param key * @return */ private final boolean addSlot(final int slot, final int key) { if (slot < allocated) { moveElementsRight(keys, slot); } allocated++; keys[slot] = key; return true; } /** * Returns the first (lowest) element currently in this set. */ public int first() { return keys[0]; } /** * Returns the last (highest) element currently in this set. */ public int last() { if (allocated == 0) return NULL_VALUE; //数组的容量-1, 就是数组已填充的最后一个元素的索引 return keys[allocated-1]; } /** * Returns the greatest element in this set less than or equal to the given element, or NULL_VALUE if there is no such element. * @param key */ public int floor(final int key) { return getRoundKey(key, false, true); } /** * Returns the least element in this set greater than or equal to the given element, or NULL_VALUE if there is no such element. * @param key */ public int ceiling(final int key) { return getRoundKey(key, true, true); } /** * Returns the greatest element in this set strictly less than the given element, or NULL_VALUE if there is no such element. * @param key */ public int lower(final int key) { return getRoundKey(key, false, false); } /** * Returns the least element in this set strictly greater than the given element, or NULL_VALUE if there is no such element. * @param key */ public int higher(final int key) { return getRoundKey(key, true, false); } /** * find key * @param key * @param upORdown * @param acceptEqual * @return */ private final int getRoundKey(final int key, final boolean upORdown, final boolean acceptEqual) { if (isEmpty()) return NULL_VALUE; int slot = findSlotByKey(key); if (upORdown) { // ceiling / higher slot = ((slot < 0) ? (-slot)-1 : (acceptEqual ? slot : slot+1)); if (slot >= allocated) { return NULL_VALUE; } } else { // floor / lower slot = ((slot < 0) ? (-slot)-2 : (acceptEqual ? slot : slot-1)); if (slot < 0) { return NULL_VALUE; } } return keys[slot]; } /** * Returns true if this set contains the specified element. * @param key * @return */ public boolean contains(final int key) { int slot = findSlotByKey(key); return (slot >= 0); } /** * Returns the element at the specified position in his internal array. * @param slot * @return */ public int get(final int slot) { if ((slot < 0) || (slot >= allocated)) return NULL_VALUE; return keys[slot]; } /** * Returns the number of elements in this set (its cardinality). * @return */ public int size() { return allocated; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < allocated; i++) { final int k = keys[i]; sb.append(k).append("|"); } if (allocated > 0) sb.setLength(sb.length() - 1); sb.append("]"); return sb.toString(); } public IntIterator iterator() { return new IntIterator(this); } static class IntIterator { final SortedIntArraySet associatedSet; int nextEntry = 0; int lastReturned = -1; public IntIterator(final SortedIntArraySet associatedSet) { this.associatedSet = associatedSet; nextEntry = 0; } public boolean hasNext() { return (nextEntry < associatedSet.allocated); } public void remove() { if (lastReturned == -1) throw new IllegalStateException(); associatedSet.removeSlot(lastReturned); lastReturned = -1; } public int next() { lastReturned = nextEntry; return associatedSet.keys[nextEntry++]; } } /** * Test * @param args */ public static void main(final String[] args) { SortedIntArraySet s = new SortedIntArraySet(3); s.put(90); s.put(10); s.put(20); s.put(30); System.out.println("toString()=" + s.toString()); s.remove(10); s.put(40); System.out.println("toString()=" + s.toString()); System.out.println("first=" + s.first()); System.out.println("last()=" + s.last()); System.out.println("floor(15)=" + s.floor(15)); System.out.println("ceiling(15)=" + s.ceiling(15)); System.out.println("lower(15)=" + s.lower(15)); System.out.println("higher(15)=" + s.higher(15)); System.out.println("floor(20)=" + s.floor(20)); System.out.println("ceiling(20)=" + s.ceiling(20)); System.out.println("lower(20)=" + s.lower(20)); System.out.println("higher(20)=" + s.higher(20)); System.out.println("floor(0)=" + s.floor(0)); System.out.println("ceiling(0)=" + s.ceiling(0)); System.out.println("lower(0)=" + s.lower(0)); System.out.println("higher(0)=" + s.higher(0)); System.out.println("floor(9999)=" + s.floor(9999)); System.out.println("ceiling(9999)=" + s.ceiling(9999)); System.out.println("lower(9999)=" + s.lower(9999)); System.out.println("higher(9999)=" + s.higher(9999)); System.out.println("contains(20)=" + s.contains(20)); System.out.println("contains(9999)=" + s.contains(9999)); System.out.println("size()=" + s.size()); System.out.println("-------- iter begin"); IntIterator iter = s.iterator(); while (iter.hasNext()) { System.out.println(iter.next()); } System.out.println("-------- iter end"); System.out.println("-------- for begin"); for (int i = -1; i <= s.size(); i++) { System.out.println(s.get(i)); } System.out.println("-------- for end"); s.clear(); System.out.println("first=" + s.first()); System.out.println("last()=" + s.last()); } }