17dd252788645e940eada959bdde927426e2531c9Paul Duffin/* 27dd252788645e940eada959bdde927426e2531c9Paul Duffin * Copyright (C) 2011 The Guava Authors 37dd252788645e940eada959bdde927426e2531c9Paul Duffin * 47dd252788645e940eada959bdde927426e2531c9Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License"); 57dd252788645e940eada959bdde927426e2531c9Paul Duffin * you may not use this file except in compliance with the License. 67dd252788645e940eada959bdde927426e2531c9Paul Duffin * You may obtain a copy of the License at 77dd252788645e940eada959bdde927426e2531c9Paul Duffin * 87dd252788645e940eada959bdde927426e2531c9Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0 97dd252788645e940eada959bdde927426e2531c9Paul Duffin * 107dd252788645e940eada959bdde927426e2531c9Paul Duffin * Unless required by applicable law or agreed to in writing, software 117dd252788645e940eada959bdde927426e2531c9Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS, 127dd252788645e940eada959bdde927426e2531c9Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137dd252788645e940eada959bdde927426e2531c9Paul Duffin * See the License for the specific language governing permissions and 147dd252788645e940eada959bdde927426e2531c9Paul Duffin * limitations under the License. 157dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 167dd252788645e940eada959bdde927426e2531c9Paul Duffin 177dd252788645e940eada959bdde927426e2531c9Paul Duffinpackage com.google.common.collect; 187dd252788645e940eada959bdde927426e2531c9Paul Duffin 197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkArgument; 200888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.collect.CollectPreconditions.checkNonnegative; 217dd252788645e940eada959bdde927426e2531c9Paul Duffin 220888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.caliper.BeforeExperiment; 230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.caliper.Benchmark; 247dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.caliper.Param; 257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.annotations.VisibleForTesting; 267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.primitives.Ints; 277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.util.concurrent.ThreadFactoryBuilder; 287dd252788645e940eada959bdde927426e2531c9Paul Duffin 297dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Iterator; 307dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.List; 317dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Map; 327dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Random; 337dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.Set; 347dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.Callable; 357dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.ConcurrentHashMap; 367dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.ConcurrentMap; 377dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.ExecutionException; 387dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.ExecutorService; 397dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.Executors; 407dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.Future; 417dd252788645e940eada959bdde927426e2531c9Paul Duffin 427dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.Nullable; 437dd252788645e940eada959bdde927426e2531c9Paul Duffin 447dd252788645e940eada959bdde927426e2531c9Paul Duffin/** 457dd252788645e940eada959bdde927426e2531c9Paul Duffin * Benchmarks for {@link ConcurrentHashMultiset}. 467dd252788645e940eada959bdde927426e2531c9Paul Duffin * 477dd252788645e940eada959bdde927426e2531c9Paul Duffin * @author mike nonemacher 487dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 490888a09821a98ac0680fad765217302858e70fa4Paul Duffinpublic class ConcurrentHashMultisetBenchmark { 507dd252788645e940eada959bdde927426e2531c9Paul Duffin @Param({"1", "2", "4", "8"}) int threads; 517dd252788645e940eada959bdde927426e2531c9Paul Duffin @Param({"3", "30", "300"}) int size; 527dd252788645e940eada959bdde927426e2531c9Paul Duffin @Param MultisetSupplier implSupplier; 537dd252788645e940eada959bdde927426e2531c9Paul Duffin 547dd252788645e940eada959bdde927426e2531c9Paul Duffin private Multiset<Integer> multiset; 557dd252788645e940eada959bdde927426e2531c9Paul Duffin private ImmutableList<Integer> keys; 567dd252788645e940eada959bdde927426e2531c9Paul Duffin private ExecutorService threadPool; 577dd252788645e940eada959bdde927426e2531c9Paul Duffin 580888a09821a98ac0680fad765217302858e70fa4Paul Duffin @BeforeExperiment void setUp() throws Exception { 597dd252788645e940eada959bdde927426e2531c9Paul Duffin multiset = implSupplier.get(); 607dd252788645e940eada959bdde927426e2531c9Paul Duffin ImmutableList.Builder<Integer> builder = ImmutableList.builder(); 617dd252788645e940eada959bdde927426e2531c9Paul Duffin for (int i = 0; i < size; i++) { 627dd252788645e940eada959bdde927426e2531c9Paul Duffin builder.add(i); 637dd252788645e940eada959bdde927426e2531c9Paul Duffin } 647dd252788645e940eada959bdde927426e2531c9Paul Duffin keys = builder.build(); 657dd252788645e940eada959bdde927426e2531c9Paul Duffin threadPool = 667dd252788645e940eada959bdde927426e2531c9Paul Duffin Executors.newFixedThreadPool(threads, new ThreadFactoryBuilder().setDaemon(true).build()); 677dd252788645e940eada959bdde927426e2531c9Paul Duffin } 687dd252788645e940eada959bdde927426e2531c9Paul Duffin 690888a09821a98ac0680fad765217302858e70fa4Paul Duffin @Benchmark long add(final int reps) throws ExecutionException, InterruptedException { 707dd252788645e940eada959bdde927426e2531c9Paul Duffin return doMultithreadedLoop( 717dd252788645e940eada959bdde927426e2531c9Paul Duffin new Callable<Long>() { 727dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public Long call() { 737dd252788645e940eada959bdde927426e2531c9Paul Duffin return runAddSingleThread(reps); 747dd252788645e940eada959bdde927426e2531c9Paul Duffin } 757dd252788645e940eada959bdde927426e2531c9Paul Duffin }); 767dd252788645e940eada959bdde927426e2531c9Paul Duffin } 777dd252788645e940eada959bdde927426e2531c9Paul Duffin 780888a09821a98ac0680fad765217302858e70fa4Paul Duffin @Benchmark long addRemove(final int reps) throws ExecutionException, InterruptedException { 797dd252788645e940eada959bdde927426e2531c9Paul Duffin return doMultithreadedLoop( 807dd252788645e940eada959bdde927426e2531c9Paul Duffin new Callable<Long>() { 817dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public Long call() { 827dd252788645e940eada959bdde927426e2531c9Paul Duffin return runAddRemoveSingleThread(reps); 837dd252788645e940eada959bdde927426e2531c9Paul Duffin } 847dd252788645e940eada959bdde927426e2531c9Paul Duffin }); 857dd252788645e940eada959bdde927426e2531c9Paul Duffin } 867dd252788645e940eada959bdde927426e2531c9Paul Duffin 877dd252788645e940eada959bdde927426e2531c9Paul Duffin private long doMultithreadedLoop(Callable<Long> task) 887dd252788645e940eada959bdde927426e2531c9Paul Duffin throws InterruptedException, ExecutionException { 897dd252788645e940eada959bdde927426e2531c9Paul Duffin 907dd252788645e940eada959bdde927426e2531c9Paul Duffin List<Future<Long>> futures = Lists.newArrayListWithCapacity(threads); 917dd252788645e940eada959bdde927426e2531c9Paul Duffin for (int i = 0; i < threads; i++) { 927dd252788645e940eada959bdde927426e2531c9Paul Duffin futures.add(threadPool.submit(task)); 937dd252788645e940eada959bdde927426e2531c9Paul Duffin } 947dd252788645e940eada959bdde927426e2531c9Paul Duffin long total = 0; 957dd252788645e940eada959bdde927426e2531c9Paul Duffin for (Future<Long> future : futures) { 967dd252788645e940eada959bdde927426e2531c9Paul Duffin total += future.get(); 977dd252788645e940eada959bdde927426e2531c9Paul Duffin } 987dd252788645e940eada959bdde927426e2531c9Paul Duffin return total; 997dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1007dd252788645e940eada959bdde927426e2531c9Paul Duffin 1017dd252788645e940eada959bdde927426e2531c9Paul Duffin private long runAddSingleThread(int reps) { 1027dd252788645e940eada959bdde927426e2531c9Paul Duffin Random random = new Random(); 1037dd252788645e940eada959bdde927426e2531c9Paul Duffin int nKeys = keys.size(); 1047dd252788645e940eada959bdde927426e2531c9Paul Duffin long blah = 0; 1057dd252788645e940eada959bdde927426e2531c9Paul Duffin for (int i = 0; i < reps; i++) { 1067dd252788645e940eada959bdde927426e2531c9Paul Duffin Integer key = keys.get(random.nextInt(nKeys)); 1077dd252788645e940eada959bdde927426e2531c9Paul Duffin int delta = random.nextInt(5); 1087dd252788645e940eada959bdde927426e2531c9Paul Duffin blah += delta; 1097dd252788645e940eada959bdde927426e2531c9Paul Duffin multiset.add(key, delta); 1107dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1117dd252788645e940eada959bdde927426e2531c9Paul Duffin return blah; 1127dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1137dd252788645e940eada959bdde927426e2531c9Paul Duffin 1147dd252788645e940eada959bdde927426e2531c9Paul Duffin private long runAddRemoveSingleThread(int reps) { 1157dd252788645e940eada959bdde927426e2531c9Paul Duffin Random random = new Random(); 1167dd252788645e940eada959bdde927426e2531c9Paul Duffin int nKeys = keys.size(); 1177dd252788645e940eada959bdde927426e2531c9Paul Duffin long blah = 0; 1187dd252788645e940eada959bdde927426e2531c9Paul Duffin for (int i = 0; i < reps; i++) { 1197dd252788645e940eada959bdde927426e2531c9Paul Duffin Integer key = keys.get(random.nextInt(nKeys)); 1207dd252788645e940eada959bdde927426e2531c9Paul Duffin // This range is [-5, 4] - slight negative bias so we often hit zero, which brings the 1217dd252788645e940eada959bdde927426e2531c9Paul Duffin // auto-removal of zeroes into play. 1227dd252788645e940eada959bdde927426e2531c9Paul Duffin int delta = random.nextInt(10) - 5; 1237dd252788645e940eada959bdde927426e2531c9Paul Duffin blah += delta; 1247dd252788645e940eada959bdde927426e2531c9Paul Duffin if (delta >= 0) { 1257dd252788645e940eada959bdde927426e2531c9Paul Duffin multiset.add(key, delta); 1267dd252788645e940eada959bdde927426e2531c9Paul Duffin } else { 1277dd252788645e940eada959bdde927426e2531c9Paul Duffin multiset.remove(key, -delta); 1287dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1297dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1307dd252788645e940eada959bdde927426e2531c9Paul Duffin return blah; 1317dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1327dd252788645e940eada959bdde927426e2531c9Paul Duffin 1337dd252788645e940eada959bdde927426e2531c9Paul Duffin private enum MultisetSupplier { 1347dd252788645e940eada959bdde927426e2531c9Paul Duffin CONCURRENT_HASH_MULTISET() { 1357dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override Multiset<Integer> get() { 1367dd252788645e940eada959bdde927426e2531c9Paul Duffin return ConcurrentHashMultiset.create(); 1377dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1387dd252788645e940eada959bdde927426e2531c9Paul Duffin }, 1397dd252788645e940eada959bdde927426e2531c9Paul Duffin BOXED_ATOMIC_REPLACE() { 1407dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override Multiset<Integer> get() { 1417dd252788645e940eada959bdde927426e2531c9Paul Duffin return OldConcurrentHashMultiset.create(); 1427dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1437dd252788645e940eada959bdde927426e2531c9Paul Duffin }, 1447dd252788645e940eada959bdde927426e2531c9Paul Duffin SYNCHRONIZED_MULTISET() { 1457dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override Multiset<Integer> get() { 1467dd252788645e940eada959bdde927426e2531c9Paul Duffin return Synchronized.multiset(HashMultiset.<Integer>create(), null); 1477dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1487dd252788645e940eada959bdde927426e2531c9Paul Duffin }, 1497dd252788645e940eada959bdde927426e2531c9Paul Duffin ; 1507dd252788645e940eada959bdde927426e2531c9Paul Duffin 1517dd252788645e940eada959bdde927426e2531c9Paul Duffin abstract Multiset<Integer> get(); 1527dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1537dd252788645e940eada959bdde927426e2531c9Paul Duffin 1547dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 1557dd252788645e940eada959bdde927426e2531c9Paul Duffin * Duplication of the old version of ConcurrentHashMultiset (with some unused stuff removed, like 1567dd252788645e940eada959bdde927426e2531c9Paul Duffin * serialization code) which used a map with boxed integers for the values. 1577dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 1587dd252788645e940eada959bdde927426e2531c9Paul Duffin private static final class OldConcurrentHashMultiset<E> extends AbstractMultiset<E> { 1597dd252788645e940eada959bdde927426e2531c9Paul Duffin /** The number of occurrences of each element. */ 1607dd252788645e940eada959bdde927426e2531c9Paul Duffin private final transient ConcurrentMap<E, Integer> countMap; 1617dd252788645e940eada959bdde927426e2531c9Paul Duffin 1627dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 1637dd252788645e940eada959bdde927426e2531c9Paul Duffin * Creates a new, empty {@code OldConcurrentHashMultiset} using the default 1647dd252788645e940eada959bdde927426e2531c9Paul Duffin * initial capacity, load factor, and concurrency settings. 1657dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 1667dd252788645e940eada959bdde927426e2531c9Paul Duffin public static <E> OldConcurrentHashMultiset<E> create() { 1677dd252788645e940eada959bdde927426e2531c9Paul Duffin return new OldConcurrentHashMultiset<E>(new ConcurrentHashMap<E, Integer>()); 1687dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1697dd252788645e940eada959bdde927426e2531c9Paul Duffin 1707dd252788645e940eada959bdde927426e2531c9Paul Duffin @VisibleForTesting OldConcurrentHashMultiset(ConcurrentMap<E, Integer> countMap) { 1717dd252788645e940eada959bdde927426e2531c9Paul Duffin checkArgument(countMap.isEmpty()); 1727dd252788645e940eada959bdde927426e2531c9Paul Duffin this.countMap = countMap; 1737dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1747dd252788645e940eada959bdde927426e2531c9Paul Duffin 1757dd252788645e940eada959bdde927426e2531c9Paul Duffin // Query Operations 1767dd252788645e940eada959bdde927426e2531c9Paul Duffin 1777dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 1787dd252788645e940eada959bdde927426e2531c9Paul Duffin * Returns the number of occurrences of {@code element} in this multiset. 1797dd252788645e940eada959bdde927426e2531c9Paul Duffin * 1807dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param element the element to look for 1817dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return the nonnegative number of occurrences of the element 1827dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 1837dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public int count(@Nullable Object element) { 1847dd252788645e940eada959bdde927426e2531c9Paul Duffin try { 1857dd252788645e940eada959bdde927426e2531c9Paul Duffin return unbox(countMap.get(element)); 1867dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (NullPointerException e) { 1877dd252788645e940eada959bdde927426e2531c9Paul Duffin return 0; 1887dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (ClassCastException e) { 1897dd252788645e940eada959bdde927426e2531c9Paul Duffin return 0; 1907dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1917dd252788645e940eada959bdde927426e2531c9Paul Duffin } 1927dd252788645e940eada959bdde927426e2531c9Paul Duffin 1937dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 1947dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@inheritDoc} 1957dd252788645e940eada959bdde927426e2531c9Paul Duffin * 1967dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>If the data in the multiset is modified by any other threads during this 1977dd252788645e940eada959bdde927426e2531c9Paul Duffin * method, it is undefined which (if any) of these modifications will be 1987dd252788645e940eada959bdde927426e2531c9Paul Duffin * reflected in the result. 1997dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 2007dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public int size() { 2017dd252788645e940eada959bdde927426e2531c9Paul Duffin long sum = 0L; 2027dd252788645e940eada959bdde927426e2531c9Paul Duffin for (Integer value : countMap.values()) { 2037dd252788645e940eada959bdde927426e2531c9Paul Duffin sum += value; 2047dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2057dd252788645e940eada959bdde927426e2531c9Paul Duffin return Ints.saturatedCast(sum); 2067dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2077dd252788645e940eada959bdde927426e2531c9Paul Duffin 2087dd252788645e940eada959bdde927426e2531c9Paul Duffin /* 2097dd252788645e940eada959bdde927426e2531c9Paul Duffin * Note: the superclass toArray() methods assume that size() gives a correct 2107dd252788645e940eada959bdde927426e2531c9Paul Duffin * answer, which ours does not. 2117dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 2127dd252788645e940eada959bdde927426e2531c9Paul Duffin 2137dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public Object[] toArray() { 2147dd252788645e940eada959bdde927426e2531c9Paul Duffin return snapshot().toArray(); 2157dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2167dd252788645e940eada959bdde927426e2531c9Paul Duffin 2177dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public <T> T[] toArray(T[] array) { 2187dd252788645e940eada959bdde927426e2531c9Paul Duffin return snapshot().toArray(array); 2197dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2207dd252788645e940eada959bdde927426e2531c9Paul Duffin 2217dd252788645e940eada959bdde927426e2531c9Paul Duffin /* 2227dd252788645e940eada959bdde927426e2531c9Paul Duffin * We'd love to use 'new ArrayList(this)' or 'list.addAll(this)', but 2237dd252788645e940eada959bdde927426e2531c9Paul Duffin * either of these would recurse back to us again! 2247dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 2257dd252788645e940eada959bdde927426e2531c9Paul Duffin private List<E> snapshot() { 2267dd252788645e940eada959bdde927426e2531c9Paul Duffin List<E> list = Lists.newArrayListWithExpectedSize(size()); 2277dd252788645e940eada959bdde927426e2531c9Paul Duffin for (Multiset.Entry<E> entry : entrySet()) { 2287dd252788645e940eada959bdde927426e2531c9Paul Duffin E element = entry.getElement(); 2297dd252788645e940eada959bdde927426e2531c9Paul Duffin for (int i = entry.getCount(); i > 0; i--) { 2307dd252788645e940eada959bdde927426e2531c9Paul Duffin list.add(element); 2317dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2327dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2337dd252788645e940eada959bdde927426e2531c9Paul Duffin return list; 2347dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2357dd252788645e940eada959bdde927426e2531c9Paul Duffin 2367dd252788645e940eada959bdde927426e2531c9Paul Duffin // Modification Operations 2377dd252788645e940eada959bdde927426e2531c9Paul Duffin 2387dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 2397dd252788645e940eada959bdde927426e2531c9Paul Duffin * Adds a number of occurrences of the specified element to this multiset. 2407dd252788645e940eada959bdde927426e2531c9Paul Duffin * 2417dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param element the element to add 2427dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param occurrences the number of occurrences to add 2437dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return the previous count of the element before the operation; possibly 2447dd252788645e940eada959bdde927426e2531c9Paul Duffin * zero 2457dd252788645e940eada959bdde927426e2531c9Paul Duffin * @throws IllegalArgumentException if {@code occurrences} is negative, or if 2467dd252788645e940eada959bdde927426e2531c9Paul Duffin * the resulting amount would exceed {@link Integer#MAX_VALUE} 2477dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 2487dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public int add(E element, int occurrences) { 2497dd252788645e940eada959bdde927426e2531c9Paul Duffin if (occurrences == 0) { 2507dd252788645e940eada959bdde927426e2531c9Paul Duffin return count(element); 2517dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2527dd252788645e940eada959bdde927426e2531c9Paul Duffin checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); 2537dd252788645e940eada959bdde927426e2531c9Paul Duffin 2547dd252788645e940eada959bdde927426e2531c9Paul Duffin while (true) { 2557dd252788645e940eada959bdde927426e2531c9Paul Duffin int current = count(element); 2567dd252788645e940eada959bdde927426e2531c9Paul Duffin if (current == 0) { 2577dd252788645e940eada959bdde927426e2531c9Paul Duffin if (countMap.putIfAbsent(element, occurrences) == null) { 2587dd252788645e940eada959bdde927426e2531c9Paul Duffin return 0; 2597dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2607dd252788645e940eada959bdde927426e2531c9Paul Duffin } else { 2617dd252788645e940eada959bdde927426e2531c9Paul Duffin checkArgument(occurrences <= Integer.MAX_VALUE - current, 2627dd252788645e940eada959bdde927426e2531c9Paul Duffin "Overflow adding %s occurrences to a count of %s", 2637dd252788645e940eada959bdde927426e2531c9Paul Duffin occurrences, current); 2647dd252788645e940eada959bdde927426e2531c9Paul Duffin int next = current + occurrences; 2657dd252788645e940eada959bdde927426e2531c9Paul Duffin if (countMap.replace(element, current, next)) { 2667dd252788645e940eada959bdde927426e2531c9Paul Duffin return current; 2677dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2687dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2697dd252788645e940eada959bdde927426e2531c9Paul Duffin // If we're still here, there was a race, so just try again. 2707dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2717dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2727dd252788645e940eada959bdde927426e2531c9Paul Duffin 2737dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 2747dd252788645e940eada959bdde927426e2531c9Paul Duffin * Removes a number of occurrences of the specified element from this 2757dd252788645e940eada959bdde927426e2531c9Paul Duffin * multiset. If the multiset contains fewer than this number of occurrences to 2767dd252788645e940eada959bdde927426e2531c9Paul Duffin * begin with, all occurrences will be removed. 2777dd252788645e940eada959bdde927426e2531c9Paul Duffin * 2787dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param element the element whose occurrences should be removed 2797dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param occurrences the number of occurrences of the element to remove 2807dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return the count of the element before the operation; possibly zero 2817dd252788645e940eada959bdde927426e2531c9Paul Duffin * @throws IllegalArgumentException if {@code occurrences} is negative 2827dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 2837dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public int remove(@Nullable Object element, int occurrences) { 2847dd252788645e940eada959bdde927426e2531c9Paul Duffin if (occurrences == 0) { 2857dd252788645e940eada959bdde927426e2531c9Paul Duffin return count(element); 2867dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2877dd252788645e940eada959bdde927426e2531c9Paul Duffin checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); 2887dd252788645e940eada959bdde927426e2531c9Paul Duffin 2897dd252788645e940eada959bdde927426e2531c9Paul Duffin while (true) { 2907dd252788645e940eada959bdde927426e2531c9Paul Duffin int current = count(element); 2917dd252788645e940eada959bdde927426e2531c9Paul Duffin if (current == 0) { 2927dd252788645e940eada959bdde927426e2531c9Paul Duffin return 0; 2937dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2947dd252788645e940eada959bdde927426e2531c9Paul Duffin if (occurrences >= current) { 2957dd252788645e940eada959bdde927426e2531c9Paul Duffin if (countMap.remove(element, current)) { 2967dd252788645e940eada959bdde927426e2531c9Paul Duffin return current; 2977dd252788645e940eada959bdde927426e2531c9Paul Duffin } 2987dd252788645e940eada959bdde927426e2531c9Paul Duffin } else { 2997dd252788645e940eada959bdde927426e2531c9Paul Duffin // We know it's an "E" because it already exists in the map. 3007dd252788645e940eada959bdde927426e2531c9Paul Duffin @SuppressWarnings("unchecked") 3017dd252788645e940eada959bdde927426e2531c9Paul Duffin E casted = (E) element; 3027dd252788645e940eada959bdde927426e2531c9Paul Duffin 3037dd252788645e940eada959bdde927426e2531c9Paul Duffin if (countMap.replace(casted, current, current - occurrences)) { 3047dd252788645e940eada959bdde927426e2531c9Paul Duffin return current; 3057dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3067dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3077dd252788645e940eada959bdde927426e2531c9Paul Duffin // If we're still here, there was a race, so just try again. 3087dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3097dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3107dd252788645e940eada959bdde927426e2531c9Paul Duffin 3117dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 3127dd252788645e940eada959bdde927426e2531c9Paul Duffin * Removes <b>all</b> occurrences of the specified element from this multiset. 3137dd252788645e940eada959bdde927426e2531c9Paul Duffin * This method complements {@link Multiset#remove(Object)}, which removes only 3147dd252788645e940eada959bdde927426e2531c9Paul Duffin * one occurrence at a time. 3157dd252788645e940eada959bdde927426e2531c9Paul Duffin * 3167dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param element the element whose occurrences should all be removed 3177dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return the number of occurrences successfully removed, possibly zero 3187dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 3197dd252788645e940eada959bdde927426e2531c9Paul Duffin private int removeAllOccurrences(@Nullable Object element) { 3207dd252788645e940eada959bdde927426e2531c9Paul Duffin try { 3217dd252788645e940eada959bdde927426e2531c9Paul Duffin return unbox(countMap.remove(element)); 3227dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (NullPointerException e) { 3237dd252788645e940eada959bdde927426e2531c9Paul Duffin return 0; 3247dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (ClassCastException e) { 3257dd252788645e940eada959bdde927426e2531c9Paul Duffin return 0; 3267dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3277dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3287dd252788645e940eada959bdde927426e2531c9Paul Duffin 3297dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 3307dd252788645e940eada959bdde927426e2531c9Paul Duffin * Removes exactly the specified number of occurrences of {@code element}, or 3317dd252788645e940eada959bdde927426e2531c9Paul Duffin * makes no change if this is not possible. 3327dd252788645e940eada959bdde927426e2531c9Paul Duffin * 3337dd252788645e940eada959bdde927426e2531c9Paul Duffin * <p>This method, in contrast to {@link #remove(Object, int)}, has no effect 3347dd252788645e940eada959bdde927426e2531c9Paul Duffin * when the element count is smaller than {@code occurrences}. 3357dd252788645e940eada959bdde927426e2531c9Paul Duffin * 3367dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param element the element to remove 3377dd252788645e940eada959bdde927426e2531c9Paul Duffin * @param occurrences the number of occurrences of {@code element} to remove 3387dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return {@code true} if the removal was possible (including if {@code 3397dd252788645e940eada959bdde927426e2531c9Paul Duffin * occurrences} is zero) 3407dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 3417dd252788645e940eada959bdde927426e2531c9Paul Duffin public boolean removeExactly(@Nullable Object element, int occurrences) { 3427dd252788645e940eada959bdde927426e2531c9Paul Duffin if (occurrences == 0) { 3437dd252788645e940eada959bdde927426e2531c9Paul Duffin return true; 3447dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3457dd252788645e940eada959bdde927426e2531c9Paul Duffin checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); 3467dd252788645e940eada959bdde927426e2531c9Paul Duffin 3477dd252788645e940eada959bdde927426e2531c9Paul Duffin while (true) { 3487dd252788645e940eada959bdde927426e2531c9Paul Duffin int current = count(element); 3497dd252788645e940eada959bdde927426e2531c9Paul Duffin if (occurrences > current) { 3507dd252788645e940eada959bdde927426e2531c9Paul Duffin return false; 3517dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3527dd252788645e940eada959bdde927426e2531c9Paul Duffin if (occurrences == current) { 3537dd252788645e940eada959bdde927426e2531c9Paul Duffin if (countMap.remove(element, occurrences)) { 3547dd252788645e940eada959bdde927426e2531c9Paul Duffin return true; 3557dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3567dd252788645e940eada959bdde927426e2531c9Paul Duffin } else { 3577dd252788645e940eada959bdde927426e2531c9Paul Duffin @SuppressWarnings("unchecked") // it's in the map, must be an "E" 3587dd252788645e940eada959bdde927426e2531c9Paul Duffin E casted = (E) element; 3597dd252788645e940eada959bdde927426e2531c9Paul Duffin if (countMap.replace(casted, current, current - occurrences)) { 3607dd252788645e940eada959bdde927426e2531c9Paul Duffin return true; 3617dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3627dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3637dd252788645e940eada959bdde927426e2531c9Paul Duffin // If we're still here, there was a race, so just try again. 3647dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3657dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3667dd252788645e940eada959bdde927426e2531c9Paul Duffin 3677dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 3687dd252788645e940eada959bdde927426e2531c9Paul Duffin * Adds or removes occurrences of {@code element} such that the {@link #count} 3697dd252788645e940eada959bdde927426e2531c9Paul Duffin * of the element becomes {@code count}. 3707dd252788645e940eada959bdde927426e2531c9Paul Duffin * 3717dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return the count of {@code element} in the multiset before this call 3727dd252788645e940eada959bdde927426e2531c9Paul Duffin * @throws IllegalArgumentException if {@code count} is negative 3737dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 3747dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public int setCount(E element, int count) { 3757dd252788645e940eada959bdde927426e2531c9Paul Duffin checkNonnegative(count, "count"); 3767dd252788645e940eada959bdde927426e2531c9Paul Duffin return (count == 0) 3777dd252788645e940eada959bdde927426e2531c9Paul Duffin ? removeAllOccurrences(element) 3787dd252788645e940eada959bdde927426e2531c9Paul Duffin : unbox(countMap.put(element, count)); 3797dd252788645e940eada959bdde927426e2531c9Paul Duffin } 3807dd252788645e940eada959bdde927426e2531c9Paul Duffin 3817dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 3827dd252788645e940eada959bdde927426e2531c9Paul Duffin * Sets the number of occurrences of {@code element} to {@code newCount}, but 3837dd252788645e940eada959bdde927426e2531c9Paul Duffin * only if the count is currently {@code oldCount}. If {@code element} does 3847dd252788645e940eada959bdde927426e2531c9Paul Duffin * not appear in the multiset exactly {@code oldCount} times, no changes will 3857dd252788645e940eada959bdde927426e2531c9Paul Duffin * be made. 3867dd252788645e940eada959bdde927426e2531c9Paul Duffin * 3877dd252788645e940eada959bdde927426e2531c9Paul Duffin * @return {@code true} if the change was successful. This usually indicates 3887dd252788645e940eada959bdde927426e2531c9Paul Duffin * that the multiset has been modified, but not always: in the case that 3897dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@code oldCount == newCount}, the method will return {@code true} if 3907dd252788645e940eada959bdde927426e2531c9Paul Duffin * the condition was met. 3917dd252788645e940eada959bdde927426e2531c9Paul Duffin * @throws IllegalArgumentException if {@code oldCount} or {@code newCount} is 3927dd252788645e940eada959bdde927426e2531c9Paul Duffin * negative 3937dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 3947dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public boolean setCount(E element, int oldCount, int newCount) { 3957dd252788645e940eada959bdde927426e2531c9Paul Duffin checkNonnegative(oldCount, "oldCount"); 3967dd252788645e940eada959bdde927426e2531c9Paul Duffin checkNonnegative(newCount, "newCount"); 3977dd252788645e940eada959bdde927426e2531c9Paul Duffin if (newCount == 0) { 3987dd252788645e940eada959bdde927426e2531c9Paul Duffin if (oldCount == 0) { 3997dd252788645e940eada959bdde927426e2531c9Paul Duffin // No change to make, but must return true if the element is not present 4007dd252788645e940eada959bdde927426e2531c9Paul Duffin return !countMap.containsKey(element); 4017dd252788645e940eada959bdde927426e2531c9Paul Duffin } else { 4027dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.remove(element, oldCount); 4037dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4047dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4057dd252788645e940eada959bdde927426e2531c9Paul Duffin if (oldCount == 0) { 4067dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.putIfAbsent(element, newCount) == null; 4077dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4087dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.replace(element, oldCount, newCount); 4097dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4107dd252788645e940eada959bdde927426e2531c9Paul Duffin 4117dd252788645e940eada959bdde927426e2531c9Paul Duffin // Views 4127dd252788645e940eada959bdde927426e2531c9Paul Duffin 4137dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override Set<E> createElementSet() { 4147dd252788645e940eada959bdde927426e2531c9Paul Duffin final Set<E> delegate = countMap.keySet(); 4157dd252788645e940eada959bdde927426e2531c9Paul Duffin return new ForwardingSet<E>() { 4167dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override protected Set<E> delegate() { 4177dd252788645e940eada959bdde927426e2531c9Paul Duffin return delegate; 4187dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4197dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public boolean remove(Object object) { 4207dd252788645e940eada959bdde927426e2531c9Paul Duffin try { 4217dd252788645e940eada959bdde927426e2531c9Paul Duffin return delegate.remove(object); 4227dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (NullPointerException e) { 4237dd252788645e940eada959bdde927426e2531c9Paul Duffin return false; 4247dd252788645e940eada959bdde927426e2531c9Paul Duffin } catch (ClassCastException e) { 4257dd252788645e940eada959bdde927426e2531c9Paul Duffin return false; 4267dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4277dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4287dd252788645e940eada959bdde927426e2531c9Paul Duffin }; 4297dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4307dd252788645e940eada959bdde927426e2531c9Paul Duffin 4317dd252788645e940eada959bdde927426e2531c9Paul Duffin private transient EntrySet entrySet; 4327dd252788645e940eada959bdde927426e2531c9Paul Duffin 4337dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public Set<Multiset.Entry<E>> entrySet() { 4347dd252788645e940eada959bdde927426e2531c9Paul Duffin EntrySet result = entrySet; 4357dd252788645e940eada959bdde927426e2531c9Paul Duffin if (result == null) { 4367dd252788645e940eada959bdde927426e2531c9Paul Duffin entrySet = result = new EntrySet(); 4377dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4387dd252788645e940eada959bdde927426e2531c9Paul Duffin return result; 4397dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4407dd252788645e940eada959bdde927426e2531c9Paul Duffin 4417dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override int distinctElements() { 4427dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.size(); 4437dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4447dd252788645e940eada959bdde927426e2531c9Paul Duffin 4457dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public boolean isEmpty() { 4467dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.isEmpty(); 4477dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4487dd252788645e940eada959bdde927426e2531c9Paul Duffin 4497dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override Iterator<Entry<E>> entryIterator() { 4507dd252788645e940eada959bdde927426e2531c9Paul Duffin final Iterator<Map.Entry<E, Integer>> backingIterator = 4517dd252788645e940eada959bdde927426e2531c9Paul Duffin countMap.entrySet().iterator(); 4527dd252788645e940eada959bdde927426e2531c9Paul Duffin return new Iterator<Entry<E>>() { 4537dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public boolean hasNext() { 4547dd252788645e940eada959bdde927426e2531c9Paul Duffin return backingIterator.hasNext(); 4557dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4567dd252788645e940eada959bdde927426e2531c9Paul Duffin 4577dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public Multiset.Entry<E> next() { 4587dd252788645e940eada959bdde927426e2531c9Paul Duffin Map.Entry<E, Integer> backingEntry = backingIterator.next(); 4597dd252788645e940eada959bdde927426e2531c9Paul Duffin return Multisets.immutableEntry(backingEntry.getKey(), 4607dd252788645e940eada959bdde927426e2531c9Paul Duffin backingEntry.getValue()); 4617dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4627dd252788645e940eada959bdde927426e2531c9Paul Duffin 4637dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public void remove() { 4647dd252788645e940eada959bdde927426e2531c9Paul Duffin backingIterator.remove(); 4657dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4667dd252788645e940eada959bdde927426e2531c9Paul Duffin }; 4677dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4687dd252788645e940eada959bdde927426e2531c9Paul Duffin 4697dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public void clear() { 4707dd252788645e940eada959bdde927426e2531c9Paul Duffin countMap.clear(); 4717dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4727dd252788645e940eada959bdde927426e2531c9Paul Duffin 4737dd252788645e940eada959bdde927426e2531c9Paul Duffin private class EntrySet extends AbstractMultiset<E>.EntrySet { 4747dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override Multiset<E> multiset() { 4757dd252788645e940eada959bdde927426e2531c9Paul Duffin return OldConcurrentHashMultiset.this; 4767dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4777dd252788645e940eada959bdde927426e2531c9Paul Duffin 4787dd252788645e940eada959bdde927426e2531c9Paul Duffin /* 4797dd252788645e940eada959bdde927426e2531c9Paul Duffin * Note: the superclass toArray() methods assume that size() gives a correct 4807dd252788645e940eada959bdde927426e2531c9Paul Duffin * answer, which ours does not. 4817dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 4827dd252788645e940eada959bdde927426e2531c9Paul Duffin 4837dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public Object[] toArray() { 4847dd252788645e940eada959bdde927426e2531c9Paul Duffin return snapshot().toArray(); 4857dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4867dd252788645e940eada959bdde927426e2531c9Paul Duffin 4877dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public <T> T[] toArray(T[] array) { 4887dd252788645e940eada959bdde927426e2531c9Paul Duffin return snapshot().toArray(array); 4897dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4907dd252788645e940eada959bdde927426e2531c9Paul Duffin 4917dd252788645e940eada959bdde927426e2531c9Paul Duffin private List<Multiset.Entry<E>> snapshot() { 4927dd252788645e940eada959bdde927426e2531c9Paul Duffin List<Multiset.Entry<E>> list = Lists.newArrayListWithExpectedSize(size()); 4937dd252788645e940eada959bdde927426e2531c9Paul Duffin // not Iterables.addAll(list, this), because that'll forward back here 4947dd252788645e940eada959bdde927426e2531c9Paul Duffin Iterators.addAll(list, iterator()); 4957dd252788645e940eada959bdde927426e2531c9Paul Duffin return list; 4967dd252788645e940eada959bdde927426e2531c9Paul Duffin } 4977dd252788645e940eada959bdde927426e2531c9Paul Duffin 4987dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public boolean remove(Object object) { 4997dd252788645e940eada959bdde927426e2531c9Paul Duffin if (object instanceof Multiset.Entry) { 5007dd252788645e940eada959bdde927426e2531c9Paul Duffin Multiset.Entry<?> entry = (Multiset.Entry<?>) object; 5017dd252788645e940eada959bdde927426e2531c9Paul Duffin Object element = entry.getElement(); 5027dd252788645e940eada959bdde927426e2531c9Paul Duffin int entryCount = entry.getCount(); 5037dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.remove(element, entryCount); 5047dd252788645e940eada959bdde927426e2531c9Paul Duffin } 5057dd252788645e940eada959bdde927426e2531c9Paul Duffin return false; 5067dd252788645e940eada959bdde927426e2531c9Paul Duffin } 5077dd252788645e940eada959bdde927426e2531c9Paul Duffin 5087dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 5097dd252788645e940eada959bdde927426e2531c9Paul Duffin * The hash code is the same as countMap's, though the objects aren't equal. 5107dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 5117dd252788645e940eada959bdde927426e2531c9Paul Duffin @Override public int hashCode() { 5127dd252788645e940eada959bdde927426e2531c9Paul Duffin return countMap.hashCode(); 5137dd252788645e940eada959bdde927426e2531c9Paul Duffin } 5147dd252788645e940eada959bdde927426e2531c9Paul Duffin } 5157dd252788645e940eada959bdde927426e2531c9Paul Duffin 5167dd252788645e940eada959bdde927426e2531c9Paul Duffin /** 5177dd252788645e940eada959bdde927426e2531c9Paul Duffin * We use a special form of unboxing that treats null as zero. 5187dd252788645e940eada959bdde927426e2531c9Paul Duffin */ 5197dd252788645e940eada959bdde927426e2531c9Paul Duffin private static int unbox(@Nullable Integer i) { 5207dd252788645e940eada959bdde927426e2531c9Paul Duffin return (i == null) ? 0 : i; 5217dd252788645e940eada959bdde927426e2531c9Paul Duffin } 5227dd252788645e940eada959bdde927426e2531c9Paul Duffin } 5237dd252788645e940eada959bdde927426e2531c9Paul Duffin} 524