guava

Форк
0
/
TestsForMapsInJavaUtil.java 
599 строк · 21.6 Кб
1
/*
2
 * Copyright (C) 2009 The Guava Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.google.common.collect.testing;
18

19
import static java.util.Arrays.asList;
20

21
import com.google.common.annotations.GwtIncompatible;
22
import com.google.common.collect.testing.features.CollectionFeature;
23
import com.google.common.collect.testing.features.CollectionSize;
24
import com.google.common.collect.testing.features.MapFeature;
25
import com.google.common.collect.testing.testers.MapEntrySetTester;
26
import java.io.Serializable;
27
import java.lang.reflect.Method;
28
import java.util.Collection;
29
import java.util.Collections;
30
import java.util.Comparator;
31
import java.util.EnumMap;
32
import java.util.HashMap;
33
import java.util.Hashtable;
34
import java.util.LinkedHashMap;
35
import java.util.Map;
36
import java.util.Map.Entry;
37
import java.util.NavigableMap;
38
import java.util.SortedMap;
39
import java.util.TreeMap;
40
import java.util.concurrent.ConcurrentHashMap;
41
import java.util.concurrent.ConcurrentSkipListMap;
42
import junit.framework.Test;
43
import junit.framework.TestSuite;
44

45
/**
46
 * Generates a test suite covering the {@link Map} implementations in the {@link java.util} package.
47
 * Can be subclassed to specify tests that should be suppressed.
48
 *
49
 * @author Kevin Bourrillion
50
 */
51
@GwtIncompatible
52
public class TestsForMapsInJavaUtil {
53

54
  public static Test suite() {
55
    return new TestsForMapsInJavaUtil().allTests();
56
  }
57

58
  public Test allTests() {
59
    TestSuite suite = new TestSuite("java.util Maps");
60
    suite.addTest(testsForCheckedMap());
61
    suite.addTest(testsForCheckedNavigableMap());
62
    suite.addTest(testsForCheckedSortedMap());
63
    suite.addTest(testsForEmptyMap());
64
    suite.addTest(testsForEmptyNavigableMap());
65
    suite.addTest(testsForEmptySortedMap());
66
    suite.addTest(testsForSingletonMap());
67
    suite.addTest(testsForHashMap());
68
    suite.addTest(testsForHashtable());
69
    suite.addTest(testsForLinkedHashMap());
70
    suite.addTest(testsForSynchronizedNavigableMap());
71
    suite.addTest(testsForTreeMapNatural());
72
    suite.addTest(testsForTreeMapWithComparator());
73
    suite.addTest(testsForUnmodifiableMap());
74
    suite.addTest(testsForUnmodifiableNavigableMap());
75
    suite.addTest(testsForUnmodifiableSortedMap());
76
    suite.addTest(testsForEnumMap());
77
    suite.addTest(testsForConcurrentHashMap());
78
    suite.addTest(testsForConcurrentSkipListMapNatural());
79
    suite.addTest(testsForConcurrentSkipListMapWithComparator());
80
    return suite;
81
  }
82

83
  protected Collection<Method> suppressForCheckedMap() {
84
    return Collections.emptySet();
85
  }
86

87
  protected Collection<Method> suppressForCheckedNavigableMap() {
88
    return Collections.emptySet();
89
  }
90

91
  protected Collection<Method> suppressForCheckedSortedMap() {
92
    return Collections.emptySet();
93
  }
94

95
  protected Collection<Method> suppressForEmptyMap() {
96
    return Collections.emptySet();
97
  }
98

99
  private Collection<Method> suppressForEmptyNavigableMap() {
100
    return Collections.emptySet();
101
  }
102

103
  private Collection<Method> suppressForEmptySortedMap() {
104
    return Collections.emptySet();
105
  }
106

107
  protected Collection<Method> suppressForSingletonMap() {
108
    return Collections.emptySet();
109
  }
110

111
  protected Collection<Method> suppressForHashMap() {
112
    return Collections.emptySet();
113
  }
114

115
  protected Collection<Method> suppressForHashtable() {
116
    return Collections.emptySet();
117
  }
118

119
  protected Collection<Method> suppressForLinkedHashMap() {
120
    return Collections.emptySet();
121
  }
122

123
  protected Collection<Method> suppressForSynchronizedNavigableMap() {
124
    return Collections.emptySet();
125
  }
126

127
  protected Collection<Method> suppressForTreeMapNatural() {
128
    return Collections.emptySet();
129
  }
130

131
  protected Collection<Method> suppressForTreeMapWithComparator() {
132
    return Collections.emptySet();
133
  }
134

135
  protected Collection<Method> suppressForUnmodifiableMap() {
136
    return Collections.emptySet();
137
  }
138

139
  protected Collection<Method> suppressForUnmodifiableNavigableMap() {
140
    return Collections.emptySet();
141
  }
142

143
  protected Collection<Method> suppressForUnmodifiableSortedMap() {
144
    return Collections.emptySet();
145
  }
146

147
  protected Collection<Method> suppressForEnumMap() {
148
    return Collections.emptySet();
149
  }
150

151
  protected Collection<Method> suppressForConcurrentHashMap() {
152
    return Collections.emptySet();
153
  }
154

155
  protected Collection<Method> suppressForConcurrentSkipListMap() {
156
    return asList(
157
        MapEntrySetTester.getSetValueMethod(),
158
        MapEntrySetTester.getSetValueWithNullValuesAbsentMethod(),
159
        MapEntrySetTester.getSetValueWithNullValuesPresentMethod());
160
  }
161

162
  public Test testsForCheckedMap() {
163
    return MapTestSuiteBuilder.using(
164
            new TestStringMapGenerator() {
165
              @Override
166
              protected Map<String, String> create(Entry<String, String>[] entries) {
167
                Map<String, String> map = populate(new HashMap<String, String>(), entries);
168
                return Collections.checkedMap(map, String.class, String.class);
169
              }
170
            })
171
        .named("checkedMap/HashMap")
172
        .withFeatures(
173
            MapFeature.GENERAL_PURPOSE,
174
            MapFeature.ALLOWS_NULL_KEYS,
175
            MapFeature.ALLOWS_NULL_VALUES,
176
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
177
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
178
            MapFeature.RESTRICTS_KEYS,
179
            MapFeature.RESTRICTS_VALUES,
180
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
181
            CollectionFeature.SERIALIZABLE,
182
            CollectionSize.ANY)
183
        .suppressing(suppressForCheckedMap())
184
        .createTestSuite();
185
  }
186

187
  public Test testsForCheckedNavigableMap() {
188
    return SortedMapTestSuiteBuilder.using(
189
            new TestStringSortedMapGenerator() {
190
              @Override
191
              protected NavigableMap<String, String> create(Entry<String, String>[] entries) {
192
                NavigableMap<String, String> map = populate(new TreeMap<String, String>(), entries);
193
                return Collections.checkedNavigableMap(map, String.class, String.class);
194
              }
195
            })
196
        .named("checkedNavigableMap/TreeMap, natural")
197
        .withFeatures(
198
            MapFeature.GENERAL_PURPOSE,
199
            MapFeature.ALLOWS_NULL_VALUES,
200
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
201
            MapFeature.RESTRICTS_KEYS,
202
            MapFeature.RESTRICTS_VALUES,
203
            CollectionFeature.KNOWN_ORDER,
204
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
205
            CollectionFeature.SERIALIZABLE,
206
            CollectionSize.ANY)
207
        .suppressing(suppressForCheckedNavigableMap())
208
        .createTestSuite();
209
  }
210

211
  public Test testsForCheckedSortedMap() {
212
    return SortedMapTestSuiteBuilder.using(
213
            new TestStringSortedMapGenerator() {
214
              @Override
215
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
216
                SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries);
217
                return Collections.checkedSortedMap(map, String.class, String.class);
218
              }
219
            })
220
        .named("checkedSortedMap/TreeMap, natural")
221
        .withFeatures(
222
            MapFeature.GENERAL_PURPOSE,
223
            MapFeature.ALLOWS_NULL_VALUES,
224
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
225
            MapFeature.RESTRICTS_KEYS,
226
            MapFeature.RESTRICTS_VALUES,
227
            CollectionFeature.KNOWN_ORDER,
228
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
229
            CollectionFeature.SERIALIZABLE,
230
            CollectionSize.ANY)
231
        .suppressing(suppressForCheckedSortedMap())
232
        .createTestSuite();
233
  }
234

235
  public Test testsForEmptyMap() {
236
    return MapTestSuiteBuilder.using(
237
            new TestStringMapGenerator() {
238
              @Override
239
              protected Map<String, String> create(Entry<String, String>[] entries) {
240
                return Collections.emptyMap();
241
              }
242
            })
243
        .named("emptyMap")
244
        .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO)
245
        .suppressing(suppressForEmptyMap())
246
        .createTestSuite();
247
  }
248

249
  public Test testsForEmptyNavigableMap() {
250
    return MapTestSuiteBuilder.using(
251
            new TestStringSortedMapGenerator() {
252
              @Override
253
              protected NavigableMap<String, String> create(Entry<String, String>[] entries) {
254
                return Collections.emptyNavigableMap();
255
              }
256
            })
257
        .named("emptyNavigableMap")
258
        .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO)
259
        .suppressing(suppressForEmptyNavigableMap())
260
        .createTestSuite();
261
  }
262

263
  public Test testsForEmptySortedMap() {
264
    return MapTestSuiteBuilder.using(
265
            new TestStringSortedMapGenerator() {
266
              @Override
267
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
268
                return Collections.emptySortedMap();
269
              }
270
            })
271
        .named("emptySortedMap")
272
        .withFeatures(CollectionFeature.SERIALIZABLE, CollectionSize.ZERO)
273
        .suppressing(suppressForEmptySortedMap())
274
        .createTestSuite();
275
  }
276

277
  public Test testsForSingletonMap() {
278
    return MapTestSuiteBuilder.using(
279
            new TestStringMapGenerator() {
280
              @Override
281
              protected Map<String, String> create(Entry<String, String>[] entries) {
282
                return Collections.singletonMap(entries[0].getKey(), entries[0].getValue());
283
              }
284
            })
285
        .named("singletonMap")
286
        .withFeatures(
287
            MapFeature.ALLOWS_NULL_KEYS,
288
            MapFeature.ALLOWS_NULL_VALUES,
289
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
290
            CollectionFeature.SERIALIZABLE,
291
            CollectionSize.ONE)
292
        .suppressing(suppressForSingletonMap())
293
        .createTestSuite();
294
  }
295

296
  public Test testsForHashMap() {
297
    return MapTestSuiteBuilder.using(
298
            new TestStringMapGenerator() {
299
              @Override
300
              protected Map<String, String> create(Entry<String, String>[] entries) {
301
                return toHashMap(entries);
302
              }
303
            })
304
        .named("HashMap")
305
        .withFeatures(
306
            MapFeature.GENERAL_PURPOSE,
307
            MapFeature.ALLOWS_NULL_KEYS,
308
            MapFeature.ALLOWS_NULL_VALUES,
309
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
310
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
311
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
312
            CollectionFeature.SERIALIZABLE,
313
            CollectionSize.ANY)
314
        .suppressing(suppressForHashMap())
315
        .createTestSuite();
316
  }
317

318
  public Test testsForHashtable() {
319
    return MapTestSuiteBuilder.using(
320
            new TestStringMapGenerator() {
321
              @Override
322
              protected Map<String, String> create(Entry<String, String>[] entries) {
323
                return populate(new Hashtable<String, String>(), entries);
324
              }
325
            })
326
        .withFeatures(
327
            MapFeature.GENERAL_PURPOSE,
328
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
329
            MapFeature.RESTRICTS_KEYS,
330
            MapFeature.SUPPORTS_REMOVE,
331
            CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
332
            CollectionFeature.SERIALIZABLE,
333
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
334
            CollectionFeature.SUPPORTS_REMOVE,
335
            CollectionSize.ANY)
336
        .named("Hashtable")
337
        .suppressing(suppressForHashtable())
338
        .createTestSuite();
339
  }
340

341
  public Test testsForLinkedHashMap() {
342
    return MapTestSuiteBuilder.using(
343
            new TestStringMapGenerator() {
344
              @Override
345
              protected Map<String, String> create(Entry<String, String>[] entries) {
346
                return populate(new LinkedHashMap<String, String>(), entries);
347
              }
348
            })
349
        .named("LinkedHashMap")
350
        .withFeatures(
351
            MapFeature.GENERAL_PURPOSE,
352
            MapFeature.ALLOWS_NULL_KEYS,
353
            MapFeature.ALLOWS_NULL_VALUES,
354
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
355
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
356
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
357
            CollectionFeature.KNOWN_ORDER,
358
            CollectionFeature.SERIALIZABLE,
359
            CollectionSize.ANY)
360
        .suppressing(suppressForLinkedHashMap())
361
        .createTestSuite();
362
  }
363

364
  /**
365
   * Tests regular NavigableMap behavior of synchronizedNavigableMap(treeMap); does not test the
366
   * fact that it's synchronized.
367
   */
368
  public Test testsForSynchronizedNavigableMap() {
369
    return NavigableMapTestSuiteBuilder.using(
370
            new TestStringSortedMapGenerator() {
371
              @Override
372
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
373
                NavigableMap<String, String> delegate = populate(new TreeMap<>(), entries);
374
                return Collections.synchronizedNavigableMap(delegate);
375
              }
376
            })
377
        .named("synchronizedNavigableMap/TreeMap, natural")
378
        .withFeatures(
379
            MapFeature.GENERAL_PURPOSE,
380
            MapFeature.ALLOWS_NULL_VALUES,
381
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
382
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
383
            CollectionFeature.KNOWN_ORDER,
384
            CollectionFeature.SERIALIZABLE,
385
            CollectionSize.ANY)
386
        .suppressing(suppressForSynchronizedNavigableMap())
387
        .createTestSuite();
388
  }
389

390
  public Test testsForTreeMapNatural() {
391
    return NavigableMapTestSuiteBuilder.using(
392
            new TestStringSortedMapGenerator() {
393
              @Override
394
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
395
                /*
396
                 * TODO(cpovirk): it would be nice to create an input Map and use
397
                 * the copy constructor here and in the other tests
398
                 */
399
                return populate(new TreeMap<String, String>(), entries);
400
              }
401
            })
402
        .named("TreeMap, natural")
403
        .withFeatures(
404
            MapFeature.GENERAL_PURPOSE,
405
            MapFeature.ALLOWS_NULL_VALUES,
406
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
407
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
408
            CollectionFeature.KNOWN_ORDER,
409
            CollectionFeature.SERIALIZABLE,
410
            CollectionSize.ANY)
411
        .suppressing(suppressForTreeMapNatural())
412
        .createTestSuite();
413
  }
414

415
  public Test testsForTreeMapWithComparator() {
416
    return NavigableMapTestSuiteBuilder.using(
417
            new TestStringSortedMapGenerator() {
418
              @Override
419
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
420
                return populate(
421
                    new TreeMap<String, String>(arbitraryNullFriendlyComparator()), entries);
422
              }
423
            })
424
        .named("TreeMap, with comparator")
425
        .withFeatures(
426
            MapFeature.GENERAL_PURPOSE,
427
            MapFeature.ALLOWS_NULL_KEYS,
428
            MapFeature.ALLOWS_NULL_VALUES,
429
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
430
            MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION,
431
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
432
            CollectionFeature.KNOWN_ORDER,
433
            CollectionFeature.SERIALIZABLE,
434
            CollectionSize.ANY)
435
        .suppressing(suppressForTreeMapWithComparator())
436
        .createTestSuite();
437
  }
438

439
  public Test testsForUnmodifiableMap() {
440
    return MapTestSuiteBuilder.using(
441
            new TestStringMapGenerator() {
442
              @Override
443
              protected Map<String, String> create(Entry<String, String>[] entries) {
444
                return Collections.unmodifiableMap(toHashMap(entries));
445
              }
446
            })
447
        .named("unmodifiableMap/HashMap")
448
        .withFeatures(
449
            MapFeature.ALLOWS_NULL_KEYS,
450
            MapFeature.ALLOWS_NULL_VALUES,
451
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
452
            CollectionFeature.SERIALIZABLE,
453
            CollectionSize.ANY)
454
        .suppressing(suppressForUnmodifiableMap())
455
        .createTestSuite();
456
  }
457

458
  public Test testsForUnmodifiableNavigableMap() {
459
    return MapTestSuiteBuilder.using(
460
            new TestStringSortedMapGenerator() {
461
              @Override
462
              protected NavigableMap<String, String> create(Entry<String, String>[] entries) {
463
                return Collections.unmodifiableNavigableMap(populate(new TreeMap<>(), entries));
464
              }
465
            })
466
        .named("unmodifiableNavigableMap/TreeMap, natural")
467
        .withFeatures(
468
            MapFeature.ALLOWS_NULL_VALUES,
469
            CollectionFeature.KNOWN_ORDER,
470
            CollectionFeature.SERIALIZABLE,
471
            CollectionSize.ANY)
472
        .suppressing(suppressForUnmodifiableNavigableMap())
473
        .createTestSuite();
474
  }
475

476
  public Test testsForUnmodifiableSortedMap() {
477
    return MapTestSuiteBuilder.using(
478
            new TestStringSortedMapGenerator() {
479
              @Override
480
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
481
                SortedMap<String, String> map = populate(new TreeMap<String, String>(), entries);
482
                return Collections.unmodifiableSortedMap(map);
483
              }
484
            })
485
        .named("unmodifiableSortedMap/TreeMap, natural")
486
        .withFeatures(
487
            MapFeature.ALLOWS_NULL_VALUES,
488
            CollectionFeature.KNOWN_ORDER,
489
            CollectionFeature.SERIALIZABLE,
490
            CollectionSize.ANY)
491
        .suppressing(suppressForUnmodifiableSortedMap())
492
        .createTestSuite();
493
  }
494

495
  public Test testsForEnumMap() {
496
    return MapTestSuiteBuilder.using(
497
            new TestEnumMapGenerator() {
498
              @Override
499
              protected Map<AnEnum, String> create(Entry<AnEnum, String>[] entries) {
500
                return populate(new EnumMap<AnEnum, String>(AnEnum.class), entries);
501
              }
502
            })
503
        .named("EnumMap")
504
        .withFeatures(
505
            MapFeature.GENERAL_PURPOSE,
506
            MapFeature.ALLOWS_NULL_VALUES,
507
            MapFeature.RESTRICTS_KEYS,
508
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
509
            CollectionFeature.KNOWN_ORDER,
510
            CollectionFeature.SERIALIZABLE,
511
            CollectionSize.ANY)
512
        .suppressing(suppressForEnumMap())
513
        .createTestSuite();
514
  }
515

516
  public Test testsForConcurrentHashMap() {
517
    return MapTestSuiteBuilder.using(
518
            new TestStringMapGenerator() {
519
              @Override
520
              protected Map<String, String> create(Entry<String, String>[] entries) {
521
                return populate(new ConcurrentHashMap<String, String>(), entries);
522
              }
523
            })
524
        .named("ConcurrentHashMap")
525
        .withFeatures(
526
            MapFeature.GENERAL_PURPOSE,
527
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
528
            CollectionFeature.SERIALIZABLE,
529
            CollectionSize.ANY)
530
        .suppressing(suppressForConcurrentHashMap())
531
        .createTestSuite();
532
  }
533

534
  public Test testsForConcurrentSkipListMapNatural() {
535
    return NavigableMapTestSuiteBuilder.using(
536
            new TestStringSortedMapGenerator() {
537
              @Override
538
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
539
                return populate(new ConcurrentSkipListMap<String, String>(), entries);
540
              }
541
            })
542
        .named("ConcurrentSkipListMap, natural")
543
        .withFeatures(
544
            MapFeature.GENERAL_PURPOSE,
545
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
546
            CollectionFeature.KNOWN_ORDER,
547
            CollectionFeature.SERIALIZABLE,
548
            CollectionSize.ANY)
549
        .suppressing(suppressForConcurrentSkipListMap())
550
        .createTestSuite();
551
  }
552

553
  public Test testsForConcurrentSkipListMapWithComparator() {
554
    return NavigableMapTestSuiteBuilder.using(
555
            new TestStringSortedMapGenerator() {
556
              @Override
557
              protected SortedMap<String, String> create(Entry<String, String>[] entries) {
558
                return populate(
559
                    new ConcurrentSkipListMap<String, String>(arbitraryNullFriendlyComparator()),
560
                    entries);
561
              }
562
            })
563
        .named("ConcurrentSkipListMap, with comparator")
564
        .withFeatures(
565
            MapFeature.GENERAL_PURPOSE,
566
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
567
            CollectionFeature.KNOWN_ORDER,
568
            CollectionFeature.SERIALIZABLE,
569
            CollectionSize.ANY)
570
        .suppressing(suppressForConcurrentSkipListMap())
571
        .createTestSuite();
572
  }
573

574
  // TODO: IdentityHashMap, AbstractMap
575

576
  private static Map<String, String> toHashMap(Entry<String, String>[] entries) {
577
    return populate(new HashMap<String, String>(), entries);
578
  }
579

580
  // TODO: call conversion constructors or factory methods instead of using
581
  // populate() on an empty map
582
  private static <T, M extends Map<T, String>> M populate(M map, Entry<T, String>[] entries) {
583
    for (Entry<T, String> entry : entries) {
584
      map.put(entry.getKey(), entry.getValue());
585
    }
586
    return map;
587
  }
588

589
  static <T> Comparator<T> arbitraryNullFriendlyComparator() {
590
    return new NullFriendlyComparator<>();
591
  }
592

593
  private static final class NullFriendlyComparator<T> implements Comparator<T>, Serializable {
594
    @Override
595
    public int compare(T left, T right) {
596
      return String.valueOf(left).compareTo(String.valueOf(right));
597
    }
598
  }
599
}
600

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.