Skip to content

Commit b837f70

Browse files
javaqueryVicky Thakor
authored andcommitted
fix: new util method added in Collectsions, UniqueIdGenerator added
Collections - isCollectionEqual - getCardinalityMap - CardinalityHelper.java UniqueIdGenerator - UniqueIdGenerator: similar to firebase pushIds
1 parent 8b07bf7 commit b837f70

File tree

5 files changed

+358
-78
lines changed

5 files changed

+358
-78
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ plugins {
55
}
66

77
group 'com.javaquery'
8-
version '1.0.1'
8+
version '1.0.3'
99

1010
repositories {
1111
mavenCentral()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.javaquery.util;
2+
3+
/**
4+
* @author vicky.thakor
5+
* @see https://gist.githubusercontent.com/swftvsn/438b4ed68619ad1f5d1c251dc3a5af6f/raw/d29e0fb3ecab2702bf1b856c2ae217d5a43442fa/FirebasePushIdGenerator.java
6+
* @since 1.0.3
7+
*/
8+
public class UniqueIdGenerator {
9+
10+
// Modeled after base64 web-safe chars, but ordered by ASCII.
11+
private final static String PUSH_CHARS = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
12+
// We generate 72-bits of randomness which get turned into 12 characters and
13+
// appended to the timestamp to prevent collisions with other clients. We store the last
14+
// characters we generated because in the event of a collision, we'll use those same
15+
// characters except "incremented" by one.
16+
private static final int[] LAST_RAND_CHARS = new int[72];
17+
// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
18+
private static long LAST_PUSH_TIME = 0L;
19+
20+
private UniqueIdGenerator() {
21+
}
22+
23+
public static synchronized String generate() {
24+
long now = System.currentTimeMillis();
25+
boolean duplicateTime = now == LAST_PUSH_TIME;
26+
LAST_PUSH_TIME = now;
27+
28+
char[] timeStampChars = new char[8];
29+
for (int i = 7; i >= 0; i--) {
30+
timeStampChars[i] = PUSH_CHARS.charAt((int) (now % 64));
31+
now = (long) Math.floor((double) (now / 64));
32+
}
33+
34+
if (now != 0) {
35+
throw new AssertionError("We should have converted the entire timestamp.");
36+
}
37+
38+
StringBuilder id = new StringBuilder(20);
39+
for (char c : timeStampChars) {
40+
id.append(c);
41+
}
42+
43+
if (!duplicateTime) {
44+
for (int i = 0; i < 12; i++) {
45+
LAST_RAND_CHARS[i] = (int) Math.floor(Double.valueOf(Math.random() * 64).intValue());
46+
}
47+
} else {
48+
// If the timestamp hasn't changed since last push, use the same random number,
49+
//except incremented by 1.
50+
int i;
51+
for (i = 11; i >= 0 && LAST_RAND_CHARS[i] == 63; i--) {
52+
LAST_RAND_CHARS[i] = 0;
53+
}
54+
LAST_RAND_CHARS[i]++;
55+
}
56+
57+
for (int i = 0; i < 12; i++) {
58+
id.append(PUSH_CHARS.charAt(LAST_RAND_CHARS[i]));
59+
}
60+
61+
if (id.length() != 20) {
62+
throw new AssertionError("Length should be 20.");
63+
}
64+
65+
return id.substring(1);
66+
}
67+
}

src/main/java/com/javaquery/util/collection/Collections.java

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.javaquery.util.collection;
22

3+
import com.javaquery.util.Assert;
34
import com.javaquery.util.Objects;
45

56
import java.util.Collection;
7+
import java.util.HashMap;
68
import java.util.List;
79
import java.util.Map;
810
import java.util.stream.IntStream;
@@ -91,4 +93,145 @@ public static <T> Stream<List<T>> batches(List<T> source, int batchSize) {
9193
.range(0, fullChunks + 1)
9294
.mapToObj(n -> source.subList(n * batchSize, n == fullChunks ? size : (n + 1) * batchSize));
9395
}
96+
97+
/**
98+
* Note: Code imported from apache commons collection
99+
* <p>
100+
* Returns {@code true} iff the given {@link Collection}s contain
101+
* exactly the same elements with exactly the same cardinalities.
102+
* <p>
103+
* That is, iff the cardinality of <i>e</i> in <i>a</i> is
104+
* equal to the cardinality of <i>e</i> in <i>b</i>,
105+
* for each element <i>e</i> in <i>a</i> or <i>b</i>.
106+
* </p>
107+
*
108+
* @param collectionOne the first collection, must not be null
109+
* @param collectionTwo the second collection, must not be null
110+
* @return {@code true} iff the collections contain the same elements with the same cardinalities.
111+
* @throws NullPointerException if either collection is null
112+
* @since 1.0.3
113+
*/
114+
public static <E> boolean isCollectionEqual(final Collection<E> collectionOne, final Collection<E> collectionTwo) {
115+
Assert.nonNull(collectionOne, NullPointerException::new);
116+
Assert.nonNull(collectionTwo, NullPointerException::new);
117+
118+
if (collectionOne.size() != collectionTwo.size()) {
119+
return false;
120+
}
121+
122+
final CardinalityHelper<Object> helper = new CardinalityHelper<>(collectionOne, collectionTwo);
123+
if (helper.cardinalityA.size() != helper.cardinalityB.size()) {
124+
return false;
125+
}
126+
for (final Object obj : helper.cardinalityA.keySet()) {
127+
if (helper.freqA(obj) != helper.freqB(obj)) {
128+
return false;
129+
}
130+
}
131+
return true;
132+
}
133+
134+
/**
135+
* Note: Code imported from apache commons collection
136+
* Returns a {@link Map} mapping each unique element in the given
137+
* {@link Collection} to an {@link Integer} representing the number
138+
* of occurrences of that element in the {@link Collection}.
139+
* <p>
140+
* Only those elements present in the collection will appear as
141+
* keys in the map.
142+
* </p>
143+
*
144+
* @param <O> the type of object in the returned {@link Map}. This is a super type of &lt;I&gt;.
145+
* @param iterable the collection to get the cardinality map for, must not be null
146+
* @return the populated cardinality map
147+
* @throws NullPointerException if coll is null
148+
* @since 1.0.3
149+
*/
150+
public static <O> Map<O, Integer> getCardinalityMap(final Iterable<? extends O> iterable) {
151+
Assert.nonNull(iterable, NullPointerException::new);
152+
final Map<O, Integer> count = new HashMap<>();
153+
for (final O obj : iterable) {
154+
count.merge(obj, 1, Integer::sum);
155+
}
156+
return count;
157+
}
158+
159+
/**
160+
* Note: Code imported from apache commons collection
161+
* Helper class to easily access cardinality properties of two collections.
162+
*
163+
* @param <O> the element type
164+
* @since 1.0.3
165+
*/
166+
private static class CardinalityHelper<O> {
167+
168+
/**
169+
* Contains the cardinality for each object in collection A.
170+
*/
171+
final Map<O, Integer> cardinalityA;
172+
173+
/**
174+
* Contains the cardinality for each object in collection B.
175+
*/
176+
final Map<O, Integer> cardinalityB;
177+
178+
/**
179+
* Create a new CardinalityHelper for two collections.
180+
*
181+
* @param a the first collection
182+
* @param b the second collection
183+
*/
184+
CardinalityHelper(final Iterable<? extends O> a, final Iterable<? extends O> b) {
185+
cardinalityA = Collections.getCardinalityMap(a);
186+
cardinalityB = Collections.getCardinalityMap(b);
187+
}
188+
189+
/**
190+
* Returns the maximum frequency of an object.
191+
*
192+
* @param obj the object
193+
* @return the maximum frequency of the object
194+
*/
195+
public final int max(final Object obj) {
196+
return Math.max(freqA(obj), freqB(obj));
197+
}
198+
199+
/**
200+
* Returns the minimum frequency of an object.
201+
*
202+
* @param obj the object
203+
* @return the minimum frequency of the object
204+
*/
205+
public final int min(final Object obj) {
206+
return Math.min(freqA(obj), freqB(obj));
207+
}
208+
209+
/**
210+
* Returns the frequency of this object in collection A.
211+
*
212+
* @param obj the object
213+
* @return the frequency of the object in collection A
214+
*/
215+
public int freqA(final Object obj) {
216+
return getFreq(obj, cardinalityA);
217+
}
218+
219+
/**
220+
* Returns the frequency of this object in collection B.
221+
*
222+
* @param obj the object
223+
* @return the frequency of the object in collection B
224+
*/
225+
public int freqB(final Object obj) {
226+
return getFreq(obj, cardinalityB);
227+
}
228+
229+
private int getFreq(final Object obj, final Map<?, Integer> freqMap) {
230+
final Integer count = freqMap.get(obj);
231+
if (count != null) {
232+
return count;
233+
}
234+
return 0;
235+
}
236+
}
94237
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.javaquery.util;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Test;
5+
6+
/**
7+
* @author vicky.thakor
8+
* @since 1.0.3
9+
*/
10+
public class TestUniqueIdGenerator {
11+
12+
@Test
13+
public void test_generate(){
14+
Assertions.assertNotNull(UniqueIdGenerator.generate());
15+
}
16+
}

0 commit comments

Comments
 (0)