1
+ /*
2
+ * Copyright (c) 2020 Lewys Davies
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
1
22
package com .lewdev .probabilitylib ;
2
23
3
24
import java .util .Comparator ;
4
25
import java .util .Iterator ;
26
+ import java .util .NavigableSet ;
5
27
import java .util .Objects ;
28
+ import java .util .SplittableRandom ;
6
29
import java .util .TreeSet ;
7
- import java .util .concurrent .ThreadLocalRandom ;
8
30
9
31
/**
10
32
* ProbabilityCollection for retrieving random elements based on probability.
23
45
* </ul>
24
46
*
25
47
* @author Lewys Davies
26
- * @version 0.6
48
+ * @version 0.8
27
49
*
28
50
* @param <E> Type of elements
29
51
*/
30
52
public class ProbabilityCollection <E > {
31
-
32
- protected final Comparator <ProbabilitySetElement <E >> comparator =
33
- (o1 , o2 )-> Integer .compare (o1 .getIndex (), o2 .getIndex ());
34
53
35
- private final TreeSet <ProbabilitySetElement <E >> collection ;
54
+ private final NavigableSet <ProbabilitySetElement <E >> collection ;
55
+ private final SplittableRandom random = new SplittableRandom ();
36
56
37
57
private int totalProbability ;
38
58
39
59
/**
40
60
* Construct a new Probability Collection
41
61
*/
42
62
public ProbabilityCollection () {
43
- this .collection = new TreeSet <>(this . comparator );
63
+ this .collection = new TreeSet <>(Comparator . comparingInt ( ProbabilitySetElement :: getIndex ) );
44
64
this .totalProbability = 0 ;
45
65
}
46
66
@@ -52,28 +72,28 @@ public int size() {
52
72
}
53
73
54
74
/**
55
- * @return Collection contains no elements
75
+ * @return True if collection contains no elements, else False
56
76
*/
57
77
public boolean isEmpty () {
58
78
return this .collection .isEmpty ();
59
79
}
60
80
61
81
/**
62
- * @param object
63
- * @return True if the collection contains the object, else False
64
- * @throws IllegalArgumentException if object null
82
+ * @param <E> object
83
+ * @return True if collection contains the object, else False
84
+ * @throws IllegalArgumentException if object is null
65
85
*/
66
86
public boolean contains (E object ) {
67
87
if (object == null ) {
68
- throw new IllegalArgumentException ("Cannot check if null object is contained in a collection" );
88
+ throw new IllegalArgumentException ("Cannot check if null object is contained in this collection" );
69
89
}
70
90
71
91
return this .collection .stream ()
72
92
.anyMatch (entry -> entry .getObject ().equals (object ));
73
93
}
74
94
75
95
/**
76
- * @return Iterator over collection
96
+ * @return Iterator over this collection
77
97
*/
78
98
public Iterator <ProbabilitySetElement <E >> iterator () {
79
99
return this .collection .iterator ();
@@ -82,7 +102,7 @@ public Iterator<ProbabilitySetElement<E>> iterator() {
82
102
/**
83
103
* Add an object to this collection
84
104
*
85
- * @param object. Not null.
105
+ * @param <E> object. Not null.
86
106
* @param probability share. Must be greater than 0.
87
107
*
88
108
* @throws IllegalArgumentException if object is null
@@ -97,33 +117,35 @@ public void add(E object, int probability) {
97
117
throw new IllegalArgumentException ("Probability must be greater than 0" );
98
118
}
99
119
100
- this . collection . add ( new ProbabilitySetElement <E >(object , probability ) );
101
- this .totalProbability += probability ;
120
+ ProbabilitySetElement < E > entry = new ProbabilitySetElement <E >(object , probability );
121
+ entry . setIndex ( this .totalProbability + 1 ) ;
102
122
103
- this .updateIndexes ();
123
+ this .collection .add (entry );
124
+ this .totalProbability += probability ;
104
125
}
105
126
106
127
/**
107
128
* Remove a object from this collection
108
129
*
109
- * @param object
130
+ * @param <E> object
110
131
* @return True if object was removed, else False.
111
132
*
112
- * @throws IllegalArgumentException if object null
133
+ * @throws IllegalArgumentException if object is null
113
134
*/
114
135
public boolean remove (E object ) {
115
136
if (object == null ) {
116
137
throw new IllegalArgumentException ("Cannot remove null object" );
117
138
}
118
139
119
140
Iterator <ProbabilitySetElement <E >> it = this .iterator ();
120
- boolean removed = it . hasNext () ;
141
+ boolean removed = false ;
121
142
122
143
while (it .hasNext ()) {
123
144
ProbabilitySetElement <E > entry = it .next ();
124
145
if (entry .getObject ().equals (object )) {
125
146
this .totalProbability -= entry .getProbability ();
126
147
it .remove ();
148
+ removed = true ;
127
149
}
128
150
}
129
151
@@ -132,6 +154,14 @@ public boolean remove(E object) {
132
154
return removed ;
133
155
}
134
156
157
+ /**
158
+ * Remove all objects from this collection
159
+ */
160
+ public void clear () {
161
+ this .collection .clear ();
162
+ this .totalProbability = 0 ;
163
+ }
164
+
135
165
/**
136
166
* Get a random object from this collection, based on probability.
137
167
*
@@ -141,11 +171,11 @@ public boolean remove(E object) {
141
171
*/
142
172
public E get () {
143
173
if (this .isEmpty ()) {
144
- throw new IllegalStateException ("Cannot get an element out of a empty set " );
174
+ throw new IllegalStateException ("Cannot get an object out of a empty collection " );
145
175
}
146
176
147
177
ProbabilitySetElement <E > toFind = new ProbabilitySetElement <>(null , 0 );
148
- toFind .setIndex (ThreadLocalRandom . current () .nextInt (1 , this .totalProbability + 1 ));
178
+ toFind .setIndex (this . random .nextInt (1 , this .totalProbability + 1 ));
149
179
150
180
return Objects .requireNonNull (this .collection .floor (toFind ).getObject ());
151
181
}
@@ -164,7 +194,7 @@ public final int getTotalProbability() {
164
194
* We then only need to store the start index of each element,
165
195
* as we make use of the TreeSet#floor
166
196
*/
167
- private void updateIndexes () {
197
+ private final void updateIndexes () {
168
198
int previousIndex = 0 ;
169
199
170
200
for (ProbabilitySetElement <E > entry : this .collection ) {
@@ -190,7 +220,7 @@ final static class ProbabilitySetElement<T> {
190
220
private int index ;
191
221
192
222
/**
193
- * @param object
223
+ * @param <T> object
194
224
* @param probability
195
225
*/
196
226
protected ProbabilitySetElement (T object , int probability ) {
@@ -199,7 +229,7 @@ protected ProbabilitySetElement(T object, int probability) {
199
229
}
200
230
201
231
/**
202
- * @return The actual object
232
+ * @return <T> The actual object
203
233
*/
204
234
public final T getObject () {
205
235
return this .object ;
0 commit comments