15
15
*/
16
16
package rx .subjects ;
17
17
18
+ import java .util .ArrayList ;
19
+ import java .util .Arrays ;
18
20
import java .util .Collection ;
19
21
import java .util .Collections ;
20
- import java .util .HashMap ;
21
- import java .util .Map ;
22
+ import java .util .List ;
22
23
import java .util .concurrent .CountDownLatch ;
23
- import java .util .concurrent .atomic .AtomicBoolean ;
24
24
import java .util .concurrent .atomic .AtomicReference ;
25
25
26
26
import rx .Observable .OnSubscribeFunc ;
@@ -126,7 +126,8 @@ protected void terminate(Action1<Collection<SubjectObserver<? super T>>> onTermi
126
126
* inated)
127
127
*/
128
128
try {
129
- onTerminate .call (newState .observers .values ());
129
+ // had to circumvent type check, we know what the array contains
130
+ onTerminate .call ((Collection )newState .observersList );
130
131
} finally {
131
132
// mark that termination is completed
132
133
newState .terminationLatch .countDown ();
@@ -143,46 +144,104 @@ protected void terminate(Action1<Collection<SubjectObserver<? super T>>> onTermi
143
144
*
144
145
* @return List<Observer<T>>
145
146
*/
146
- public Collection <SubjectObserver <? super T >> snapshotOfObservers () {
147
- // we don't need to copy since state is immutable
148
- return state .get ().observers .values ();
147
+ private Collection <SubjectObserver <? super T >> snapshotOfObservers () {
148
+ // had to circumvent type check, we know what the array contains
149
+ return (Collection )state .get ().observersList ;
150
+ }
151
+ /**
152
+ * Returns the array of observers directly.
153
+ * <em>Don't modify the array!</em>
154
+ * @return the array of current observers
155
+ */
156
+ public SubjectObserver <Object >[] rawSnapshot () {
157
+ return state .get ().observers ;
149
158
}
150
159
151
160
protected static class State <T > {
152
161
final boolean terminated ;
153
162
final CountDownLatch terminationLatch ;
154
- final Map <Subscription , SubjectObserver <? super T >> observers ;
155
-
156
- private State (boolean isTerminated , CountDownLatch terminationLatch , Map <Subscription , SubjectObserver <? super T >> observers ) {
163
+ final Subscription [] subscriptions ;
164
+ final SubjectObserver <Object >[] observers ;
165
+ // to avoid lots of empty arrays
166
+ final Subscription [] EMPTY_S = new Subscription [0 ];
167
+ @ SuppressWarnings ("rawtypes" )
168
+ // to avoid lots of empty arrays
169
+ final SubjectObserver [] EMPTY_O = new SubjectObserver [0 ];
170
+ @ SuppressWarnings ("rawtypes" )
171
+ final List <SubjectObserver <Object >> observersList ;
172
+ private State (boolean isTerminated , CountDownLatch terminationLatch ,
173
+ Subscription [] subscriptions , SubjectObserver [] observers ) {
157
174
this .terminationLatch = terminationLatch ;
158
175
this .terminated = isTerminated ;
159
- this .observers = Collections .unmodifiableMap (observers );
176
+ this .subscriptions = subscriptions ;
177
+ this .observers = observers ;
178
+ this .observersList = Arrays .asList (this .observers );
160
179
}
161
180
162
181
State () {
163
182
this .terminated = false ;
164
183
this .terminationLatch = null ;
165
- this .observers = Collections .emptyMap ();
184
+ this .subscriptions = EMPTY_S ;
185
+ this .observers = EMPTY_O ;
186
+ observersList = Collections .emptyList ();
166
187
}
167
188
168
189
public State <T > terminate () {
169
190
if (terminated ) {
170
191
throw new IllegalStateException ("Already terminated." );
171
192
}
172
- return new State <T >(true , new CountDownLatch (1 ), observers );
193
+ return new State <T >(true , new CountDownLatch (1 ), subscriptions , observers );
173
194
}
174
195
175
196
public State <T > addObserver (Subscription s , SubjectObserver <? super T > observer ) {
176
- Map <Subscription , SubjectObserver <? super T >> newMap = new HashMap <Subscription , SubjectObserver <? super T >>();
177
- newMap .putAll (observers );
178
- newMap .put (s , observer );
179
- return new State <T >(terminated , terminationLatch , newMap );
197
+ int n = this .observers .length ;
198
+
199
+ Subscription [] newsubscriptions = Arrays .copyOf (this .subscriptions , n + 1 );
200
+ SubjectObserver [] newobservers = Arrays .copyOf (this .observers , n + 1 );
201
+
202
+ newsubscriptions [n ] = s ;
203
+ newobservers [n ] = observer ;
204
+
205
+ return createNewWith (newsubscriptions , newobservers );
206
+ }
207
+ private State <T > createNewWith (Subscription [] newsubscriptions , SubjectObserver [] newobservers ) {
208
+ return new State <T >(terminated , terminationLatch , newsubscriptions , newobservers );
180
209
}
181
210
182
211
public State <T > removeObserver (Subscription s ) {
183
- Map <Subscription , SubjectObserver <? super T >> newMap = new HashMap <Subscription , SubjectObserver <? super T >>(observers );
184
- newMap .remove (s );
185
- return new State <T >(terminated , terminationLatch , newMap );
212
+ // we are empty, nothing to remove
213
+ if (this .observers .length == 0 ) {
214
+ return this ;
215
+ }
216
+ int n = Math .max (this .observers .length - 1 , 1 );
217
+ int copied = 0 ;
218
+ Subscription [] newsubscriptions = Arrays .copyOf (this .subscriptions , n );
219
+ SubjectObserver [] newobservers = Arrays .copyOf (this .observers , n );
220
+
221
+ for (int i = 0 ; i < this .subscriptions .length ; i ++) {
222
+ Subscription s0 = this .subscriptions [i ];
223
+ if (s0 != s ) {
224
+ if (copied == n ) {
225
+ // if s was not found till the end of the iteration
226
+ // we return ourselves since no modification should
227
+ // have happened
228
+ return this ;
229
+ }
230
+ newsubscriptions [copied ] = s0 ;
231
+ newobservers [copied ] = this .observers [i ];
232
+ copied ++;
233
+ }
234
+ }
235
+
236
+ if (copied == 0 ) {
237
+ return createNewWith (EMPTY_S , EMPTY_O );
238
+ }
239
+ // if somehow copied less than expected, truncate the arrays
240
+ // if s is unique, this should never happen
241
+ if (copied < n ) {
242
+ return createNewWith (Arrays .copyOf (newsubscriptions , copied ), Arrays .copyOf (newobservers , copied ));
243
+ }
244
+ return createNewWith (newsubscriptions , newobservers );
186
245
}
187
246
}
188
247
0 commit comments