001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2011-2013 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.util; 011 012import mondrian.olap.Util; 013 014import java.io.Serializable; 015import java.util.*; 016 017/** 018 * Implementation of {@link java.util.SortedSet} based on an array. The array 019 * must already be sorted in natural order. 020 * 021 * @param <E> 022 * 023 * @author Julian Hyde 024 */ 025public class ArraySortedSet<E extends Comparable<E>> 026 extends AbstractSet<E> 027 implements SortedSet<E>, Serializable 028{ 029 private static final long serialVersionUID = -7613058579094914399L; 030 private final E[] values; 031 private final int start; 032 private final int end; 033 034 /** 035 * Creates a set backed by an array. The array must be sorted, and is 036 * not copied. 037 * 038 * @param values Array of values 039 */ 040 public ArraySortedSet(E[] values) { 041 this(values, 0, values.length); 042 } 043 044 /** 045 * Creates a set backed by a region of an array. The array must be 046 * sorted, and is not copied. 047 * 048 * @param values Array of values 049 * @param start Index of start of region 050 * @param end Index of first element after end of region 051 */ 052 public ArraySortedSet(E[] values, int start, int end) { 053 super(); 054 this.values = values; 055 this.start = start; 056 this.end = end; 057 } 058 059 public Iterator<E> iterator() { 060 return asList().iterator(); 061 } 062 063 public int size() { 064 return end - start; 065 } 066 067 public Comparator<? super E> comparator() { 068 return null; 069 } 070 071 public SortedSet<E> subSet(E fromElement, E toElement) { 072 int from = Util.binarySearch(values, start, end, fromElement); 073 if (from < 0) { 074 from = - (from + 1); 075 } 076 int to = Util.binarySearch(values, from, end, toElement); 077 if (to < 0) { 078 to = - (to + 1); 079 } 080 return subset(from, to); 081 } 082 083 public SortedSet<E> headSet(E toElement) { 084 int to = Util.binarySearch(values, start, end, toElement); 085 if (to < 0) { 086 to = - (to + 1); 087 } 088 return subset(start, to); 089 } 090 091 public SortedSet<E> tailSet(E fromElement) { 092 int from = Util.binarySearch(values, start, end, fromElement); 093 if (from < 0) { 094 from = - (from + 1); 095 } 096 return subset(from, end); 097 } 098 099 private SortedSet<E> subset(int from, int to) { 100 if (from == start && to == end) { 101 return this; 102 } 103 return new ArraySortedSet<E>(values, from, to); 104 } 105 106 private List<E> asList() { 107 //noinspection unchecked 108 List<E> list = Arrays.asList(values); 109 if (start > 0 || end < values.length) { 110 list = list.subList(start, end); 111 } 112 return list; 113 } 114 115 public E first() { 116 try { 117 return values[start]; 118 } catch (ArrayIndexOutOfBoundsException e) { 119 throw new NoSuchElementException(); 120 } 121 } 122 123 public E last() { 124 try { 125 return values[end - 1]; 126 } catch (ArrayIndexOutOfBoundsException e) { 127 throw new NoSuchElementException(); 128 } 129 } 130 131 @Override 132 public Object[] toArray() { 133 if (start == 0 && end == values.length) { 134 return values.clone(); 135 } else { 136 final Object[] os = new Object[end - start]; 137 System.arraycopy(values, start, os, 0, end - start); 138 return os; 139 } 140 } 141 142 @SuppressWarnings({"unchecked"}) 143 @Override 144 public <T> T[] toArray(T[] a) { 145 int size = size(); 146 T[] r = a.length >= size 147 ? a 148 : (T[]) java.lang.reflect.Array.newInstance( 149 a.getClass().getComponentType(), size); 150 //noinspection SuspiciousSystemArraycopy 151 System.arraycopy(values, start, r, 0, end - start); 152 if (r.length > size) { 153 r[size] = null; 154 } 155 return r; 156 } 157 158 /** 159 * Performs a merge between two {@link ArraySortedSet} instances 160 * in O(n) time, returning a third instance that doesn't include 161 * duplicates. 162 * 163 * <p>For example, 164 * <tt>ArraySortedSet("a", "b", "c").merge(ArraySortedSet("a", "c", 165 * "e"))</tt> returns 166 * <tt>ArraySortedSet("a", "b", "c", "e")}</tt>.</p> 167 * 168 * @param arrayToMerge Other set to combine with this 169 * @return Set containing union of the elements of inputs 170 * 171 * @see Util#intersect(java.util.SortedSet, java.util.SortedSet) 172 */ 173 public ArraySortedSet<E> merge( 174 ArraySortedSet<E> arrayToMerge) 175 { 176 assert arrayToMerge != null; 177 178 // No need to merge when one array is empty. 179 if (this.size() == 0) { 180 return arrayToMerge; 181 } 182 if (arrayToMerge.size() == 0) { 183 return this; 184 } 185 186 int p1 = 0, p2 = 0, m = 0, k = this.size() + arrayToMerge.size(); 187 188 final E[] data1 = this.values; 189 final E[] data2 = arrayToMerge.values; 190 @SuppressWarnings({"unchecked"}) 191 E[] merged = 192 Util.genericArray( 193 (Class<E>) this.values[0].getClass(), 194 k); 195 196 while (p1 < this.size() && p2 < arrayToMerge.size()) { 197 final int compare = 198 data1[p1].compareTo(data2[p2]); 199 if (compare == 0) { 200 merged[m++] = data1[p1++]; 201 p2++; 202 } else if (compare < 0) { 203 merged[m++] = data1[p1++]; 204 } else { 205 merged[m++] = data2[p2++]; 206 } 207 } 208 209 while (p1 < this.size()) { 210 merged[m++] = data1[p1++]; 211 } 212 213 while (p2 < arrayToMerge.size()) { 214 merged[m++] = data2[p2++]; 215 } 216 217 // Note that m < k if there were duplicates. Result has fewer elements 218 // than sum of inputs. But it's not worth truncating the array. 219 return new ArraySortedSet<E>(merged, 0, m); 220 } 221 222 @Override 223 public boolean contains(Object o) { 224 //noinspection unchecked 225 return o != null 226 && Util.binarySearch(values, start, end, (E) o) >= 0; 227 } 228} 229 230// End ArraySortedSet.java