Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >读 Java Arrays 源码 笔记

读 Java Arrays 源码 笔记

作者头像
yuxiaofei93
发布于 2018-09-11 08:31:10
发布于 2018-09-11 08:31:10
76400
代码可运行
举报
文章被收录于专栏:于晓飞的专栏于晓飞的专栏
运行总次数:0
代码可运行

Arrays.java是Java中用来操作数组的类。使用这个工具类可以减少平常很多的工作量。了解其实现,可以避免一些错误的用法。

它提供的操作包括:

  1. 排序 sort
  2. 查找 binarySearch()
  3. 比较 equals
  4. 填充 fill
  5. 转列表 asList()
  6. 哈希 Hash()
  7. 转字符串 toString()

这个类的代码量很多,Java1.7中有4000多行。因为每一种基本类型都做了兼容,所以整个类真正逻辑不多。下面简单介绍一下它各个功能的实现:

排序


这里的排序实现有两种

一种是为基本类型数组设计的,它的对外接口有两种,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//whole array
public static void sort(primitive[] a);

//sub array
public static void sort(primitive[] a, int fromIndex, int toIndex);

它们的具体实现方式是一样的都是调用了DualPivotQuicksort.sort(...)方法。这个方法的中文含义是 双轴快速排序。它在性能上优于传统的单轴快速排序。

算法的逻辑可以参考国外一篇博客

如果想要阅读源码可以参考我的另一篇博客双轴快速排序源码阅读笔记

它是不稳定

另一种是为Object对象设计的,它要求传进来的数组对象必须实现Comparable接口。

它提供的接口如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// whole array, default asec
public static void sort(Object[] a);

// subArray, default asec
public static void sort(Object[] a, int fromIndex, int toIndex);

还有带泛型参数的接口,它需要指定一个比较器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// whole array with comparator
public static <T> void sort(T[] a, Comparator<? super T> c);

// sub array with comparator
public static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c);

他的实现方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// java/utils/Arrays.java
static final class LegacyMergeSort {
        private static final boolean userRequested =
            java.security.AccessController.doPrivileged(
                new sun.security.action.GetBooleanAction(
                    "java.util.Arrays.useLegacyMergeSort")).booleanValue();
}

//sort 方法的实现
public static void sort(Object[] a) {
    if (LegacyMergeSort.userRequested)
       legacyMergeSort(a);
    else
       //与TimSort的逻辑是相同的
       ComparableTimSort.sort(a);
}

//legacyMergeSort
private static void legacyMergeSort(Object[] a) {
    Object[] aux = a.clone();
    mergeSort(aux, a, 0, a.length, 0);
}                    

//归并排序
    private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        // 小数组直接进行普通插入排序
        if (length < INSERTIONSORT_THRESHOLD) {
             ///...
            return;
        }

        //下面是归并排序的实现,
        ///...
    }

从上面的逻辑可以看出来,它的实现方式分为两种,一种是通过Arrays.java中的归并排序实现的,另一种采用了TimSort算法。其中Arrays.java中的归并排序的逻辑相对简单,是一种插入排序与传统归并排序的结合。当待排序的数组小于INSERTIONSROT_THERSHOLD的时候直接进行插入排序,不再递归。TimSort算法也是一种插入排序与归并排序结合的算法,不过它的细节优化要比Arrays.java中的算法做的多。详细介绍可以参考维基百科或者我的TimSort 源码笔记

两种算法的切换依靠运行时系统变量的设置。具体参考StackOverFlow上的一篇回答。我们默认情况下是不打开这个开关的,也就是说没有特殊要求的情况下,我们默认使用TimSort算法来实现排序。从注释上来看,在未来某个版本,Arrays.java中的merge方法将会被删除掉。

这个排序方法是 稳定 的。

查找

Arrays.java中只提供了二分查找。二分查找的前提就是数组是经过升序排序的,所以使用之前务必确保数组是有序的。

调用的接口比较简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static int binarySearch(primative[] a, primative key);
public static int binarySearch(primative[] a, int fromIndex, int toIndex, primative key);
public static int binarySearch(Object[] a, Object key);
public static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key);
public static <T> int binarySearch(T[] a, T key, Comparator< ? super T> c);
public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key,Comparator<? super T> c);

equals

这个也比较简单,equals方法与==的不同大家应该都很熟悉了,这里直接贴出接口:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 基本类型
    public static boolean equals(long[] a, long[] a2) {
        //...
    }
    // 对象
    public static boolean equals(Object[] a, Object[] a2) {
        //...
    }
    // 高维数组的equal比较,通过递归实现
    // 这里没有对循环引用进行检查,如果两个如组同时存在循环引用的情况下可能出现死循环的风险。
    public static boolean deepEquals(Object[] a1, Object[] a2) {
       //...
    }

fill 填充


批量初始化的时候不要自己写for循环了,已经有人帮我们写好了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 基本类型批量赋值
public static void fill(int[] a, int fromIndex, int toIndex, int val) {
    rangeCheck(a.length, fromIndex, toIndex);
    for (int i = fromIndex; i < toIndex; i++)
        a[i] = val;
}
// 对象批量赋值
public static void fill(Object[] a, int fromIndex, int toIndex, Object val) {
    rangeCheck(a.length, fromIndex, toIndex);
    for (int i = fromIndex; i < toIndex; i++)
        a[i] = val;
}

复制


数组复制的最终实现是调用了JVM中的方法。具体没有深究,但在数据量大的时候应该能快些。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// @file Arrays.java
// 基本类型的复制,从0开始到指定长度
public static byte[] copyOf(byte[] original, int newLength) {
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    return copy;
}
// 基本类型复制,从指定起点到指定终点
public static byte[] copyOfRange(byte[] original, int from, int to) {
    int newLength = to - from;
    if (newLength < 0)
        throw new IllegalArgumentException(from + " > " + to);
    byte[] copy = new byte[newLength];
    System.arraycopy(original, from, copy, 0,
                    Math.min(original.length - from, newLength));
    return copy;
}
//这里是泛型数组的复制, 结合泛型进阶中的内容,这里好理解很多。
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

// @file System.java
public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

// 

转换成列表 asList


将一组对象转换成列表,这里一定要注意返回的ArrayList并非平常用的java.util.ArrayList ,而是Arrays.java中定义的一个简单的静态内部类--ArrayList。它不支持添加和移除元素,不支持扩容。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@file java/util/Arrays.java

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

//注意,此ArrayList非平常用的ArrayList;这里的实现比较简单。
//不能扩容,所以不支持add,remove等操作。
private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable {
    /// ...
}

哈希 hash

高维数组的哈希计算,采用递归实现。同样,如果自己的某个元素直接或者间接持有自己,会出现死循环。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 基本类型哈希
    public static int hashCode(long a[]) {
       // ...
    }
    
    // 对象哈希
    public static int hashCode(Object a[]) {
       //...
    }
    
    // 高维数组哈希,采用递归实现。如果自己的某个元素直接或者间接持有自己,会出现死讯环,
    // 所以`Object[]`最好直接使用`hashcode(Object)`。
    public static int deepHashCode(Object a[]) {
       //...
    }

toString


有了这个方法,打Log的时候就不需要写循环了。

这里的高维数组直接或者间接持有自己的情况不会出现死循环。因为这里做了特殊处理,用一个Set保存了打印过的数组。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 基本类型
    public static String toString(int[] a) {
       // ...
    }
    // 对象
    public static String toString(Object[] a) {
        // ...
    }
    // 高维数组toString(). 这里处理了自我持有的问题。
    public static String deepToString(Object[] a) {
        // ...
        deepToString(a, buf, new HashSet<Object[]>());
        return buf.toString();
    }
    // 真正的实现方法, 递归实现。
    // 使用了一个Set来存储已经打印过的类,如果再次出现这个对象,直接打印[...]
    private static void deepToString(Object[] a, StringBuilder buf,
                                     Set<Object[]> dejaVu) {
       //...
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016.06.02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
(31) 剖析Arrays / 计算机程序的思维逻辑
数组是存储多个同类型元素的基本数据结构,数组中的元素在内存连续存放,可以通过数组下标直接定位任意元素,相比我们在后续章节介绍的其他容器,效率非常高。 数组操作是计算机程序中的常见基本操作,Java中有一个类Arrays,包含一些对数组操作的静态方法,本节主要就来讨论这些方法,我们先来看怎么用,然后再来看它们的实现原理。学习Arrays的用法,我们就可以避免重新发明轮子,直接使用,学习它的实现原理,我们就可以在需要的时候,自己实现它不具备的功能。 用法 toString Arrays的toString方法可
swiftma
2018/01/31
1.4K0
面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》
好的算法搭配上合适的数据结构,可以让代码功能大大的提升效率。当然,算法学习不只是刷题,还需要落地与应用,否则到了写代码的时候,还是会for循环+ifelse。
小傅哥
2020/09/16
4420
面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》
Arrays 的二分查找
  二分查找也称为折半查找,是对有序元素查找的一种算法,在查找的过程中,不断的将搜索长度减半,因此效率不错。Java 的 JDK 提供了二分法查找的算法,使用的方法是Arrays.binarySearch()。binarySearch() 方法提供了多种数据类型的二分查找,比如实现了int、float、double、char、byte 和 Object 类型,还提供了对泛型的支持。在 JavaAPI 手册中提供了接口说明,比如如下方法:
码农UP2U
2020/08/26
4630
集合框架3-Arrays 类
Arrays 和 Collections是分别操作数组和集合的两个工具类。今天就来对 Arrays 中的内容作个总结。
归思君
2023/10/16
2370
JDK错误用法—TimSort
Tim Peters在2002年设计了该算法并在Python中使用(TimSort 是Python中list.sort的默认实现),后被引入java。TimSort算法是一种归并排序和插入排序的混合排序算法,设计初衷是为了在真实世界中的各种数据中可以有较好的性能。基本工作过程是:
早安嵩骏
2020/08/11
8910
浅析:java的"排序"函数使用了哪些"算法"
针对泛型的排序方法有两个大分支,分别对应Collections.sort()的两个重载方法:
浩说编程
2022/02/23
5240
浅析:java的"排序"函数使用了哪些"算法"
【Java学习笔记之十二】Java8增强的工具类:Arrays的用法整理总结
本文将整理 java.util.Arrays 工具类比较常用的方法:  本文介绍的方法基于JDK 1.7 之上。  1.  asList方法  @SafeVarargs public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }    使用该方法可以返回一个固定大小的List,如:  List<String> stringList = Arrays.asList("Welcome", "
Angel_Kitty
2018/04/09
6700
【Java学习笔记之十二】Java8增强的工具类:Arrays的用法整理总结
Java程序员必备基础:Object的十二个知识点
构造方法是每一个类独有的,并不能被子类继承,因为构造方法没有返回值,子类定义不了和父类的构造方法一样的方法。但是在同一个类中,构造方法可以重载
捡田螺的小男孩
2020/07/16
4330
Java程序员必备基础:Object的十二个知识点
Java基础系列(四十一):集合之List
List是继承自Collection的一个子接口,它提供了一个有序的集合,在这个集合中我们可以使用索引去获取集合中的值,同时,我们也可以通过迭代器去访问集合中的元素,第一种方法被称为随机访问,因为我们可以按照任意的顺序去访问元素,而使用迭代器就必须顺序的去访问元素。
山禾说
2019/01/21
3860
关于Arrays你可能还不知道的细节
Arrays 主要对数组提供了一些高效的操作,比如说排序、二分查找、填充、拷贝、相等判断,转化为list等等。我们选择部分看下,其他的可以看jdk中的Arrays源码。
陈琛
2021/04/23
4120
Java Arrarys工具类
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:
CODER-V
2023/03/04
4370
Java 常用工具类 Collections 源码分析
张拭心 shixinzhang
2018/01/05
1.4K0
Java 常用工具类 Collections 源码分析
Java 排序遇到的神坑,我替你踩了!
作者:nxlhero 来源:https://blog.51cto.com/nxlhero/2515850
Java技术栈
2020/11/23
1.3K0
Arrays.Sort()中的那些排序算法
Arrays.Sort方法所用的排序算法主要涉及以下三种:双轴快速排序(DualPivotQuicksort)、归并排序(MergeSort)、TimSort,也同时包含了一些非基于比较的排序算法:例如计数排序。其具体最终使用哪一种排序算法通常根据类型以及输入长度来动态抉择。
Rekent
2021/03/05
9110
Arrays.Sort()中的那些排序算法
JDK1.8源码(四)——java.util.Arrays 类
  java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用。 1、asList public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }   作用是返回由指定数组支持的固定大小列表。   注意:这个方法返回的 ArrayList 不是我们常用的集合类 java.util.ArrayList。这里
IT可乐
2018/03/30
8750
JDK1.8源码(四)——java.util.Arrays 类
【两万字】面试官:听说你精通集合源码,接我二十个问题!
这个图由Map指向Collection的Produces并不是说Map是Collection的一个子类(子接口),这里的意思是指Map的KeySet获取到的一个视图是Collection的子接口。
山禾说
2020/07/24
6620
【两万字】面试官:听说你精通集合源码,接我二十个问题!
源码阅读--Collections.sort
这是一个boolean值。说白了就是,如果用户指定归并排序那就归并排序,否则就是ComparableTimSort。归并排序比较常见,就不讲了。贴一下ComparableTimSort
提莫队长
2019/02/21
5580
【Java基础】实用工具类Arrays,让使用数组更轻松。
boolean equals(int[],int[])方法: 可以用于判断两个数组是否相等,返回值是布尔类型(true或false) 案例:
.29.
2022/11/15
3090
【Java基础】实用工具类Arrays,让使用数组更轻松。
Java常用类(四)之数组工具类Arrays
前言   数组的工具类java.util.Arrays   由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。 一、Arrays类概述 1.1、Arrays类的引入   该是java.util包中的类,在我们的代码中想使用这个类的话,就必须使用import进行导入。   在当前类A中,只有java.lang包下的类,以及和当前类A在同一个包下的类,不需要import引入之外,其他所有的包下的类在被使用之前都要import引入
用户1195962
2018/01/18
1.3K0
Java常用类(四)之数组工具类Arrays
Java集合源码剖析——ArrayList源码剖析
ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。
Java团长
2018/08/03
8420
相关推荐
(31) 剖析Arrays / 计算机程序的思维逻辑
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验