zoukankan      html  css  js  c++  java
  • 【Java】【常用类】 Arrays工具类 源码学习

    虽然在数组的随笔中有说过,但实际上应该仔细深入一下源码进行分析

    源码没有想象中的高大上,代码终究还是写给人看的,可读性大于执行性

    最小阵列排序:1 乘 2的13次方 =  8192 

    学识浅薄,暂时还不明白这个常量在数组工具类的意义

    通过翻译的介绍,说明这是并行排序最小长度的要求

    【并行排序的最小数组长度】

     - 算法不会进一步划分排序任务。使用

     - 较小的大小通常会导致

     - 使并行加速不太可能的任务。

    private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;

    私有化的构造器,因为是工具类,设计者认为不应该产生实例

    【抑制默认构造函数,确保不可实例化。】

    // Suppresses default constructor, ensuring non-instantiability.
        private Arrays() {}

    一个固定的静态内部类 叫自然顺序类

    实现了可比较接口,和重写了比较方法,暂时还不知道其中的用意

    static final class NaturalOrder implements Comparator<Object> {
            @SuppressWarnings("unchecked")
            public int compare(Object first, Object second) {
                return ((Comparable<Object>)first).compareTo(second);
            }
            static final NaturalOrder INSTANCE = new NaturalOrder();
        }

    长度范围检查

    设计者认为所有工具方法,都应该先确认一下参数注入的数组的正确性

    所以设计了长度检查,如果不符合描述,直接丢异常出去交给调用者处理检查

    - 如果起始索引大于截至索引,抛出不合理的参数异常,并指明错误参数

    - 如果起始位置小于0,也就是小于第一个元素的位置索引 ,抛出越界异常

    - 如果截至位置大于数组的长度 抛出越界异常

        /**
         * Checks that {@code fromIndex} and {@code toIndex} are in
         * the range and throws an exception if they aren't.
         */
        private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
            if (fromIndex > toIndex) {
                throw new IllegalArgumentException(
                        "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
            }
            if (fromIndex < 0) {
                throw new ArrayIndexOutOfBoundsException(fromIndex);
            }
            if (toIndex > arrayLength) {
                throw new ArrayIndexOutOfBoundsException(toIndex);
            }
        }

    可以看到这个排序调用的是一个名叫【双枢轴快速排序类的排序方法】

    只看过快速排序,哪儿见过这算法,打个mark留意一下把,

    因为只注入一个数组的参数,所以不需要上面范围检查

      public static void sort(int[] a) {
            DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
        }

    如果是针对数组的一个片段的排序,这个排序的重载就会调用范围检查

        public static void sort(int[] a, int fromIndex, int toIndex) {
            rangeCheck(a.length, fromIndex, toIndex);
            DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
        }

    基本类型的重载

    除了对基本类型排序之外,还有对引用类型排序的支持!

    但是具体实现的算法还是没看懂,只能标记一下了

        public static void sort(Object[] a) {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a);
            else
                ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
        }
    
        /** To be removed in a future release. */
        private static void legacyMergeSort(Object[] a) {
            Object[] aux = a.clone();
            mergeSort(aux, a, 0, a.length, 0);
        }

    并行排序、又称串行排序,用于多线程相关的排序

    看不懂,我太菜了。。。。

    备注说明了这个方法是从1.8开始有的

        public static void parallelSort(byte[] a) {
            int n = a.length, p, g;
            if (n <= MIN_ARRAY_SORT_GRAN ||
                (p = ForkJoinPool.getCommonPoolParallelism()) == 1)
                DualPivotQuicksort.sort(a, 0, n - 1);
            else
                new ArraysParallelSortHelpers.FJByte.Sorter
                    (null, a, new byte[n], 0, n, 0,
                     ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?
                     MIN_ARRAY_SORT_GRAN : g).invoke();
        }

    也是配备了基本类型和引用类型的重载

    并行前缀方法

    跟并行排序配套使用的方法... 依旧不懂

        public static <T> void parallelPrefix(T[] array, BinaryOperator<T> op) {
            Objects.requireNonNull(op);
            if (array.length > 0)
                new ArrayPrefixHelpers.CumulateTask<>
                        (null, op, array, 0, array.length).invoke();
        }

    对应的类型只有这么几个重载

    官方的二分查找,在获取中轴游标时采用的位运算 无符号右移1,也就是除2

        // Like public version, but without range checks.
        private static int binarySearch0(long[] a, int fromIndex, int toIndex,
                                         long key) {
            int low = fromIndex;
            int high = toIndex - 1;
    
            while (low <= high) {
                int mid = (low + high) >>> 1;
                long midVal = a[mid];
    
                if (midVal < key)
                    low = mid + 1;
                else if (midVal > key)
                    high = mid - 1;
                else
                    return mid; // key found
            }
            return -(low + 1);  // key not found.
        }

    对应的基本和引用类型的重载

    比较两个数组之间是否相同  地址一样 或者 数组的长度和元素都是一样

        public static boolean equals(int[] a, int[] a2) {
            if (a==a2)
                return true;
            if (a==null || a2==null)
                return false;
    
            int length = a.length;
            if (a2.length != length)
                return false;
    
            for (int i=0; i<length; i++)
                if (a[i] != a2[i])
                    return false;
    
            return true;
        }

    引用类型数组增加了对元素对象的比较

        public static boolean equals(Object[] a, Object[] a2) {
            if (a==a2)
                return true;
            if (a==null || a2==null)
                return false;
    
            int length = a.length;
            if (a2.length != length)
                return false;
    
            for (int i=0; i<length; i++) {
                Object o1 = a[i];
                Object o2 = a2[i];
                if (!(o1==null ? o2==null : o1.equals(o2)))
                    return false;
            }
    
            return true;
        }

    比较的重载

    填充数组的重载 ,增加了范围检查设置,可以选数组的一个片段进行填充

      public static void fill(long[] a, int fromIndex, int toIndex, long val) {
            rangeCheck(a.length, fromIndex, toIndex);
            for (int i = fromIndex; i < toIndex; i++)
                a[i] = val;
        }

    复制数组直接调用的是系统类给的

    然而系统类的复制方法是调用C++的方法

    关于native描述的方法

    https://www.cnblogs.com/b3051/p/7484501.html

        public static char[] copyOf(char[] original, int newLength) {
            char[] copy = new char[newLength];
            System.arraycopy(original, 0, copy, 0,
                             Math.min(original.length, newLength));
            return copy;
        }

    片段复制

    public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
            int newLength = to - from;
            if (newLength < 0)
                throw new IllegalArgumentException(from + " > " + to);
            @SuppressWarnings("unchecked")
            T[] copy = ((Object)newType == (Object)Object[].class)
                ? (T[]) new Object[newLength]
                : (T[]) Array.newInstance(newType.getComponentType(), newLength);
            System.arraycopy(original, from, copy, 0,
                             Math.min(original.length - from, newLength));
            return copy;
        }

    转换成List集合,List是一个接口,实际上应该是由实现类完成的转换

      @SafeVarargs
        @SuppressWarnings("varargs")
        public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }

    转换成字符串形式

    - 空指针打印null

    - 如果索引个数,也就是没有元素,返回空

    - 使用Buider拼接字符串,遍历到最后返回

    还有个深转换的,针对对象设计的方法...

        public static String toString(long[] a) {
            if (a == null)
                return "null";
            int iMax = a.length - 1;
            if (iMax == -1)
                return "[]";
    
            StringBuilder b = new StringBuilder();
            b.append('[');
            for (int i = 0; ; i++) {
                b.append(a[i]);
                if (i == iMax)
                    return b.append(']').toString();
                b.append(", ");
            }
        }

    就先看到这儿了,剩下的几个方法都还没看懂是干嘛的

  • 相关阅读:
    MySQL 基本字段类型
    《将博客搬至CSDN》
    【转载·收藏】 html5手机网站自适应需要加的meta标签
    SQL LIKE操作符 Thinkphp
    Thinkphp判断值是否为空
    Thinkphp重复字段过滤
    Thinkphp框架删除确认对话框
    PHP微信公众平台开发高级篇——群发接口(慕课网学习笔记)
    通过当前cateid来判断切换tab
    js获取当前页面的url中id
  • 原文地址:https://www.cnblogs.com/mindzone/p/12714262.html
Copyright © 2011-2022 走看看