Guava 优秀源码记录

    技术2022-07-11  109

    Guava 优秀源码记录

    Maps.difference(差异)

    两个Map间 高效的差异算法(Maps.difference)其底层的实现也算是最优的实现了,只需要循环一次。入参就是两个 Map,比较之后能够返回四种差异:

    左边 Map 独有 key。右边 Map 独有 key。左右边 Map 都有 key,并且 value 相等。左右边 Map 都有 key,但是 value 不等。 // 对比两个 map 的差异 private static <K, V> void doDifference( Map<? extends K, ? extends V> left, Map<? extends K, ? extends V> right, Equivalence<? super V> valueEquivalence, // key 只在左边 map 出现 Map<K, V> onlyOnLeft, // key 只在右边 map 出现,调用 doDifference 方法前已经包含了全部右边的值 Map<K, V> onlyOnRight, // key 在左右 map 中都出现过,并且 value 都相等 Map<K, V> onBoth, // key 在左右 map 中都出现过,但 value 不等 Map<K, MapDifference.ValueDifference<V>> differences) { // 以左边 map 为基准进行循环 for (Entry<? extends K, ? extends V> entry : left.entrySet()) { K leftKey = entry.getKey(); V leftValue = entry.getValue(); // 右边 map 包含左边的 key if (right.containsKey(leftKey)) { // onlyOnRight 已经包含全部右边的值 所以需要删除当前 key V rightValue = onlyOnRight.remove(leftKey); // key 相等,并且 value 值也相等 if (valueEquivalence.equivalent(leftValue, rightValue)) { onBoth.put(leftKey, leftValue); // key 相等,但 value 值不等 } else { differences.put(leftKey, ValueDifferenceImpl.create(leftValue, rightValue)); } // 右边 map 不包含左边的 key,就是左边 map 独有的 key } else { onlyOnLeft.put(leftKey, leftValue); } } }

    demo 及结果

    HashMap<String, Object> left = new HashMap<>(); left.put("1", "1"); left.put("3", "3"); left.put("5", "555"); left.put("6", ""); left.put("8", "left"); HashMap<String, Object> right = new HashMap<>(); right.put("2", "2"); right.put("4", "4"); right.put("5", "555"); right.put("7", "7"); right.put("8", "right"); MapDifference<String, Object> difference = Maps.difference(left, right); log.info("左边 map 独有 key:{}",difference.entriesOnlyOnLeft()); log.info("右边 map 独有 key:{}",difference.entriesOnlyOnRight()); log.info("左右边 map 都有 key,并且 value 相等:{}",difference.entriesInCommon()); log.info("左右边 map 都有 key,但 value 不等:{}",difference.entriesDiffering()); // 左边 map 独有 key:{1=1, 3=3, 6=} // 右边 map 独有 key:{2=2, 4=4, 7=7} // 左右边 map 都有 key,并且 value 相等:{5=555} // 左右边 map 都有 key,但 value 不等:{8=(left, right)}

    Lists.reverse(反转)

    demo:

    public static void testReverse(){ List<String> list = new ArrayList<String>(){{ add("10"); add("20"); add("30"); add("40"); }}; log.info("反转之前:"+list); list = Lists.reverse(list); log.info("反转之后:"+list); log.info(list.getClass().getName()); } // 反转之前:[10, 20, 30, 40] // 反转之后:[40, 30, 20, 10] // com.google.common.collect.Lists$ReverseList

    reverse 方法底层实现非常巧妙,底层覆写了 List 原生的 get (index) 方法,会把传进来的 index 进行 (size - 1) - index 的计算,使计算得到的索引位置和 index 位置正好相反,这样当我们 get 时,数组索引位置的 index 已经是相反的位置了,达到了反转排序的效果,其实底层并没有进行反转排序,只是在计算相反的索引位置,通过计算相反的索引位置这样简单的设计,得到了反转排序的效果,很精妙。

    private int reverseIndex(int index) { int size = size(); checkElementIndex(index, size); return (size - 1) - index; }

    ArrayList的初始化

    当我们清楚ArrayList的大小时,减少扩容的次数,可以在创建时,指定其大小。

    // 可以预估 list 的大小为 20 List<String> list = Lists.newArrayListWithCapacity(20); // 不太肯定 list 大小是多少,但期望是大小是 20 左右。 List<String> list = Lists.newArrayListWithExpectedSize(20);

    newArrayListWithCapacity (20) 方法内部实现是:new ArrayList<>(20);,而 newArrayListWithExpectedSize 方法内部实现是对 List 大小有一个计算公式的,计算公式为:5L + arraySize + (arraySize / 10) ,arraySize 表示传进来的值,公式简化下就是 5 + 11/10 * arraySize,因为这个方法表示期望的大小,所以这里取的约是期望值的十分之十一,比传进来的值约大十分之一,所以根据 20 最终计算出来的值是 27。

    HashMap的初始化

    与ArrayList的初始化类似,创建时指定大小,是为了减少扩容次数。经典的计算初始大小的公式:取最大值(期望的值 / 0.75 + 1,默认值 16)。newHashMapWithExpectedSize 方法底层也是这么算的初始化大小的

    Map<String,String> withExpectedSizeHashMap = Maps.newHashMapWithExpectedSize(20);

    其计算容量,最终调用的是capacity方法

    static int capacity(int expectedSize) { if (expectedSize < 3) { // 检查是否负数 checkNonnegative(expectedSize, "expectedSize"); return expectedSize + 1; } // 是否小于2^30 if (expectedSize < Ints.MAX_POWER_OF_TWO) { // This is the calculation used in JDK8 to resize when a putAll // happens; it seems to be the most conservative calculation we // can make. 0.75 is the default load factor. return (int) ((float) expectedSize / 0.75F + 1.0F); } return Integer.MAX_VALUE; // any large value }
    Processed: 0.011, SQL: 9