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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
不会一致性hash算法,劝你简历别写搞过负载均衡
这两天看到技术群里,有小伙伴在讨论一致性hash算法的问题,正愁没啥写的题目就来了,那就简单介绍下它的原理。下边我们以分布式缓存中经典场景举例,面试中也是经常提及的一些话题,看看什么是一致性hash算法以及它有那些过人之处。
程序员小富
2022/02/09
3050
不会一致性hash算法,劝你简历别写搞过负载均衡
不会一致性hash算法,劝你简历别写搞过负载均衡
这两天看到技术群里,有小伙伴在讨论一致性hash算法的问题,正愁没啥写的题目就来了,那就简单介绍下它的原理。下边我们以分布式缓存中经典场景举例,面试中也是经常提及的一些话题,看看什么是一致性hash算法以及它有那些过人之处。
程序员小富
2022/01/12
3940
不会一致性hash算法,劝你简历别写搞过负载均衡
一致性hash算法原理及golang实现
这里存在一种场景, 当一个缓存服务由多个服务器组共同提供时, key应该路由到哪一个服务.这里假如采用最通用的方式key%N(N为服务器数目), 这里乍一看没什么问题, 但是当服务器数目发送增加或减少时, 分配方式则变为key%(N+1)或key%(N-1).这里将会有大量的key失效迁移,如果后端key对应的是有状态的存储数据,那么毫无疑问,这种做法将导致服务器间大量的数据迁移,从而照成服务的不稳定. 为了解决类问题,一致性hash算法应运而生.
李海彬
2018/07/26
1.2K0
一致性hash算法原理及golang实现
Redis扩容与一致性Hash算法解析
在分布式系统中,随着数据量的增加和负载的变化,对于存储系统的扩容变得尤为重要。Redis作为一种高性能的内存数据库,其在扩容方面采用了一致性Hash算法,以实现无缝的数据分布和负载均衡。本篇博客将详细探讨Redis的扩容机制,同时深入解析一致性Hash算法,并提供相应的代码示例。
疯狂的KK
2023/08/18
5190
Redis扩容与一致性Hash算法解析
面试:1~2亿条数据需要缓存,请问如何设计这个存储案例
一致性Hash算法背景,一致性哈希算法在1997年由麻省理工学院中提出的,设计目标是为了解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,自然取余数不OK了。
一个风轻云淡
2022/11/15
3010
面试:1~2亿条数据需要缓存,请问如何设计这个存储案例
Redis(二)---数据分区
分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。
Autooooooo
2020/11/09
6930
Redis(二)---数据分区
分布式缓存--一致性hash原理和hash槽,以及算法实现
我们在使用n台存储设备存储数据的时候,常规做法有将数据根据key%n这样计算放在哪台服务器,但是在扩容的时候就会遇到数据迁移的问题,比如扩容m台服务器,以前是key%n,现在是key%(n+m),导致数据存储的位置需要变化,数据迁移的成本比较大,这个时候我们就引用了一种叫一致性hash的算法 。 一致性哈希算法在1997年由麻省理工学院提出,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。
yingzi_code
2019/08/31
1.1K0
【转】什么是一致性hash算法?(详解)
最近有小伙伴跑过来问什么是Hash一致性算法,说面试的时候被问到了,因为不了解,所以就没有回答上,问我有没有相应的学习资料推荐,当时上班,没时间回复,晚上回去了就忘了这件事,今天突然看到这个,加班为大家整理一下什么是Hash一致性算法,希望对大家有帮助!
洋仔聊编程
2019/09/18
6020
【转】什么是一致性hash算法?(详解)
集群扩容的常规解决:一致性hash算法
写这篇博客是因为之前面试的一个问题: 如果memcached集群需要增加机器或者减少机器,那么其他机器上的数据怎么办? 最后了解到使用一致性hash算法可以解决,下面一起来学习下吧。 声明与致谢:   本文转载于朱双印博主的个人日志《白话解析:一致性哈希算法 consistent hashing》一文。 ---- 一. 引子   在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先来描述
一枝花算不算浪漫
2018/06/26
1.7K0
redis系列之——一致性hash算法「建议收藏」
redis系列之——分布式锁 redis系列之——缓存穿透、缓存击穿、缓存雪崩 redis系列之——Redis为什么这么快? redis系列之——数据持久化(RDB和AOF) redis系列之——一致性hash算法 redis系列之——高可用(主从、哨兵、集群) redis系列之——事物及乐观锁 redis系列之——数据类型geospatial:你隔壁有没有老王? redis系列之——数据类型bitmaps:今天你签到了吗? 布隆过滤器是个啥!
全栈程序员站长
2022/11/10
2.5K0
redis系列之——一致性hash算法「建议收藏」
一致性hash面试题_java面试算法
在学习一致性hash算法之前,首先要考虑下为什么要使用它,使用它能解决什么样的问题。带着问题去学习相信理解起来会更容易。
全栈程序员站长
2022/11/10
3120
一致性hash面试题_java面试算法
图解一致性哈希算法,看这一篇就够了!
近段时间一直在总结分布式系统架构常见的算法。前面我们介绍过布隆过滤器算法。接下来介绍一个非常重要、也非常实用的算法:一致性哈希算法。通过介绍一致性哈希算法的原理并给出了一种实现和实际运用的案例,带大家真正理解一致性哈希算法。
章为忠学架构
2023/03/23
8.5K2
图解一致性哈希算法,看这一篇就够了!
一致性 Hash 算法原理&应用梳理
前几天在技术群里,看到有小伙伴在讨论一致性hash算法的问题,正好今天以这个为话题,简单介绍下它的原理。下边我们以分布式缓存中经典场景举例,面试中也是经常提及的一些话题,看看什么是一致性hash算法以及它有那些过人之处。
架构精进之路
2022/12/21
1.1K0
一致性 Hash 算法原理&应用梳理
浅谈一致性Hash算法
大多数网站背后肯定不是只有一台服务器提供服务,因为单机的并发量和数据量都是有限的,所以都会用多台服务器构成集群来对外提供服务。
码之有理
2024/03/12
2920
一致性Hash介绍及使用场景
当单个节点(缓存服务器等)的能力达到上限,一般需要增加节点来打破瓶颈。在分布式系统中,扩容缩容操作极为常见。为了保证数据的均匀,一般情况会采用对key值hash,然后取模的方式,然后根据结果,确认数据落到哪台节点上。如:hash(key)%N,这的确实现了初步的分布式,数据均匀分散到了各个节点上,流量请求也均匀的分散到了各个节点;但出现以下情况:
码农编程进阶笔记
2022/08/18
3530
一致性Hash介绍及使用场景
一致性hash算法原理及实践
今天我们就来看看工作和面试中经常被点名的算法,一致性hash算法,并且我会介绍它在实际的应用场景并用代码实现出来。
蓝胖子的编程梦
2023/06/21
2690
一致性hash算法原理及实践
redis高可用模式比较及一致性hash
Sentinel为Redis提供高可用。利用Sentinel,在无人干预的情况下,可用让Redis服务抵御一定程度的故障。主要发挥以下几个方面的作用:
山行AI
2019/09/09
3.2K0
redis高可用模式比较及一致性hash
5分钟带你理解一致性Hash算法。
一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。 但现在一致性hash算法在分布式系统中也得到了广泛应用,研究过memcached缓存数据库的人都知道,memcached服务器端本身不提供分布式cache的一致性,而是由客户端来提供,具体在计算一致性hash时采用如
Java技术栈
2018/03/30
7380
5分钟带你理解一致性Hash算法。
图解一致性hash算法和实现
一致性hash算法,是麻省理工学院1997年提出的一种算法,目前主要应用于分布式缓存当中。 一致性hash算法可以有效地解决分布式存储结构下动态增加和删除节点所带来的问题。 在Memcached、Key-Value Store、Bittorrent DHT、LVS中都采用了一致性hash算法,可以说一致性hash算法是分布式系统负载均衡的首选算法。
全菜工程师小辉
2019/08/16
7550
docker高级篇2-分布式存储之三种算法
简单粗暴,直接有效。只需要预估好数据规划好节点。就能保证一段时间的数据支撑。使用HASH算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求,起到负载均衡+分而治之的作用。
凯哥Java
2022/12/18
4350
docker高级篇2-分布式存储之三种算法
相关推荐
不会一致性hash算法,劝你简历别写搞过负载均衡
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验