【笔记】Arrays.binarySearch()实践,以及需要注意的一些问题点

news2025/2/27 21:04:55

背景:我想校验一个指定的String字符串,是否存在于另一个String数组中,选择Arrays.binarySearch()方法实现,代码如下:

String[] item = {"0","1","16","1591","1594","1596"};
if (Arrays.binarySearch(item, "1591") > 0) {
    System.out.println("exists");
} else {
    System.out.println("not exists");
}

运行结果:

not exists

很直观的能看到item数组里面存在字符串1591,为什么程序运行的结果却是找不到该元素呢?

首先来看一下源码:

/**
 * Searches the specified array for the specified object using the binary
 * search algorithm. The array must be sorted into ascending order
 * according to the
 * {@linkplain Comparable natural ordering}
 * of its elements (as by the
 * {@link #sort(Object[])} method) prior to making this call.
 * If it is not sorted, the results are undefined.
 * (If the array contains elements that are not mutually comparable (for
 * example, strings and integers), it <i>cannot</i> be sorted according
 * to the natural ordering of its elements, hence results are undefined.)
 * If the array contains multiple
 * elements equal to the specified object, there is no guarantee which
 * one will be found.
 *
 * @param a the array to be searched
 * @param key the value to be searched for
 * @return index of the search key, if it is contained in the array;
 *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
 *         <i>insertion point</i> is defined as the point at which the
 *         key would be inserted into the array: the index of the first
 *         element greater than the key, or <tt>a.length</tt> if all
 *         elements in the array are less than the specified key.  Note
 *         that this guarantees that the return value will be &gt;= 0 if
 *         and only if the key is found.
 * @throws ClassCastException if the search key is not comparable to the
 *         elements of the array.
 */
public static int binarySearch(Object[] a, Object key) {
    return binarySearch0(a, 0, a.length, key);
}

注意,注释上提到了两个重点:

  1. 使用二分查找算法在指定数组中搜索指定对象;
  2. 在调用此方法之前,必须根据元素的自然顺序(如通过sort(Object[])方法)将数组按升序排序。

也就是说,数组不是遍历每一个元素,与目标值做对比,校验是否相同,而是通过二分查找算法,先找到数组中间的元素,与目标值做比较:如果目标值大于中间值,则继续比较数组后半部分的元素;如果目标值小于中间值,则继续比较数组前半部分的元素;如果等于,那么就直接返回中间元素的数组下标。因此在调用此方法之前,要先对数组进行升序排序。

private static int binarySearch0(Object[] a, int fromIndex, int toIndex,
                                     Object key) {
    int low = fromIndex;
    int high = toIndex - 1;

    while (low <= high) {
        int mid = (low + high) >>> 1;
        @SuppressWarnings("rawtypes")
        Comparable midVal = (Comparable)a[mid];
        @SuppressWarnings("unchecked")
        int cmp = midVal.compareTo(key);

        if (cmp < 0)
            low = mid + 1;
        else if (cmp > 0)
            high = mid - 1;
        else
            return mid; // key found
    }
    return -(low + 1);  // key not found.
}

源码中,是中间值通过(low + high) >>> 1 的方式获取的。

这是一个在二分查找算法中常见的代码片段。low 和 high 通常表示搜索范围的下界和上界。mid 是下界和上界的中间值,通过 (low + high) >>> 1 计算得出。

这是由于在Java中,+ 运算符对于整数是按照整数算术运算(即舍去小数点)来执行的。这可能会导致整数的溢出。例如,如果 low 是 -1000000000,而 high 是 1000000000,那么 low + high 的结果将会是 -999999999 + 1000000000,这将导致整数溢出。

而使用 >>>(无符号右移运算符)则可以避免这个问题。位运算中,右移运算符 >> 对于负数会将移位后的左侧填充部分填充为该数的符号位(即负数的话填充为1,正数的话填充为0)。而 >>> 是无符号右移运算符,无论该数是正数还是负数,都会将左侧填充部分填充为0。

所以 (low + high) >>> 1 的结果就是 low + high 的值除以2的整数部分,无论 low 和 high 的值是多少。

拿到数组的中间元素后,通过int cmp = midVal.compareTo(key)的方法比较中间元素和目标值。

compareTo() 方法按字典顺序比较两个字符串(比较基于字符串中每个字符的 Unicode 值)。

接下来回到最开始的问题中,通过调试发现,中间元素16与目标值1591比较,结果cmp=1,也就是说比较的结果居然是16大于1591
在这里插入图片描述
由于比较的结果大于0,因此 mid-1,接下来会拿数组左边部分的值与目标值做对比,而16左边的几个元素不存在1591,因此最终结果是在数组中找不到与目标值一致的元素。

这是因为,161591都是字符串类型,而非数值类型,字符串类型通过compareTo() 方法进行比较,是比较两个字符串相应位置字符的Unicode值。

/**
 * Compares two strings lexicographically.
 * The comparison is based on the Unicode value of each character in
 * the strings. The character sequence represented by this
 * {@code String} object is compared lexicographically to the
 * character sequence represented by the argument string. The result is
 * a negative integer if this {@code String} object
 * lexicographically precedes the argument string. The result is a
 * positive integer if this {@code String} object lexicographically
 * follows the argument string. The result is zero if the strings
 * are equal; {@code compareTo} returns {@code 0} exactly when
 * the {@link #equals(Object)} method would return {@code true}.
 * <p>
 * This is the definition of lexicographic ordering. If two strings are
 * different, then either they have different characters at some index
 * that is a valid index for both strings, or their lengths are different,
 * or both. If they have different characters at one or more index
 * positions, let <i>k</i> be the smallest such index; then the string
 * whose character at position <i>k</i> has the smaller value, as
 * determined by using the &lt; operator, lexicographically precedes the
 * other string. In this case, {@code compareTo} returns the
 * difference of the two character values at position {@code k} in
 * the two string -- that is, the value:
 * <blockquote><pre>
 * this.charAt(k)-anotherString.charAt(k)
 * </pre></blockquote>
 * If there is no index position at which they differ, then the shorter
 * string lexicographically precedes the longer string. In this case,
 * {@code compareTo} returns the difference of the lengths of the
 * strings -- that is, the value:
 * <blockquote><pre>
 * this.length()-anotherString.length()
 * </pre></blockquote>
 *
 * @param   anotherString   the {@code String} to be compared.
 * @return  the value {@code 0} if the argument string is equal to
 *          this string; a value less than {@code 0} if this string
 *          is lexicographically less than the string argument; and a
 *          value greater than {@code 0} if this string is
 *          lexicographically greater than the string argument.
 */
public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    return len1 - len2;
}

引申:但是要注意的是,这只适用于两个字符都属于基本字母或数字的情况。如果字符包含其他字符(比如特殊字符、标点符号等),或者涉及非数字字符,那么结果可能不会如你所预期。例如,如果’A’和’中’(Unicode值为65296)进行相减,结果将会是-38321,这显然不是我们期望的结果。因此,在进行这种操作时,一定要确保字符的取值范围和你的预期相符。

至此,文章开头 1591为什么在目标数组中 {"0","1","16","1591","1594","1596"} 匹配不到的问题,原因就是如此。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1158337.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java程序员面试核心知识--Java基础知识(一)

目录 一、Java程序初始化顺序 二、Java的Clone方法作用 三、 OverLoad&#xff08;重载&#xff09;与Override&#xff08;重写&#xff09;区别 四、abstract class&#xff08;抽象类&#xff09;与interface&#xff08;接口&#xff09;的异同 五、String、StringBuf…

行业追踪,2023-11-01

自动复盘 2023-11-01 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

【腾讯云 HAI域探秘】宝妈也能快速入门AI绘画

活动背景 本次活动是由腾讯云和CSDN联合推出的开发者技术实践活动。我通过技术交流直播、动手实验、征文等形式&#xff0c;深入沉浸式体验腾讯云高性能应用服务 HAI。从活动中汲取到技术上的精华。在本次活动中&#xff0c;只要完成各个环节任务&#xff0c;不仅可以参与 AIGC…

嵌入式每日500(2)231102 (GPIO外设)

文章目录 1.GPIO外设介绍&#xff08;老生常谈的那些东西&#xff09;2.GPIO的寄存器&#xff08;9个&#xff09;3.GPIO的类型定义&#xff08;结构体和枚举&#xff09;4.GPIO常量定义&#xff08;GPIO_PIN,GPIO_MODE,GPIO_SPEED,GPIO_PULL&#xff09;5.GPIO函数定义&#x…

echarts 画散点图, x周,y周在指定位置标志一下

文章目录 echarts 画散点图&#xff0c; x周&#xff0c;y周在指定位置标志一下示例一例子二示例三 echarts 画散点图&#xff0c; x周&#xff0c;y周在指定位置标志一下 示例一 let scatterData {data: [[[-0.2, -0.6],[0.4, 0.3],[0.1, 0.4],[0.3, 0.5],[0.09, 0.1],[0.7,…

仿东郊到家按摩小程序app开发搭建

最新版的仿东郊到家按摩小程序&#xff0c;支持上门服务系统&#xff0c;全开源&#xff0c;并且支持公众号、小程序和app。前端使用uniapp&#xff0c;后端使用php&#xff0c;价格合理。用户端功能模块包括技师选择、预约服务、优惠券、订单等&#xff0c;还有意见功能和城市…

Java多线程篇(13)——FutureTask、Disruptor的使用

文章目录 FutureTaskCompletionServiceCompletableFuture DisruptorDisruptor 核心概念运行流程不同生产者模式的区别Disruptor设计精髓 FutureTask 现有一个场景&#xff0c;10个线程执行10个任务&#xff0c;然后主线程获取任务结果。 比较广泛的一个说法就是&#xff0c;r…

代码随想录算法训练营第三十九天丨 动态规划part02

62.不同路径 思路 动态规划 机器人从(0 , 0) 位置出发&#xff0c;到(m - 1, n - 1)终点。 按照动规五部曲来分析&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j] &#xff1a;表示从&#xff08;0 &#xff0c;0&#xff09;出发&#…

【每日一题】参加会议的最多员工数

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;内向基环树拓扑排序分类讨论内向基环树分类讨论基环长度大于 2基环长度等于 2 功能实现 写在最后 Tag 【内向基环树拓扑排序分类讨论】【图】【2023-11-01】 题目来源 2127. 参加会议的最多员工数 题目解读 员工只有…

阿昌教你如何优雅的数据脱敏

阿昌教你如何优雅的数据脱敏 Hi&#xff0c;我是阿昌&#xff0c;最近有一个数据脱敏的需求&#xff0c;要求用户可自定义配置数据权限&#xff0c;并对某种类型数据进行脱敏返回给前端 一、涉及知识点 SpringMVCJava反射Java自定义注解Java枚举 二、方案选择 1、需求要求…

呼叫中心的重要考核指标

呼叫中心在运营过程中越来越精细化&#xff0c;在信息化管理的时代&#xff0c;呼叫中心系统是必不可少的&#xff0c;而呼叫中心的管理人员为了提升运营效率&#xff0c;通常会根据业务目标设置各种业务的考核指标&#xff0c;而我也根据OKCC在呼叫中心项目运营过程中的经验&a…

【双十一预售】玩得越来越大了...

双十一又又又到了 剁手带来的快乐终究是短暂的 让自己变得更优秀才是长远的快乐 当今大环境 工作难找&#xff0c;钱难赚 只有不断学习与成长 方能应对未来的各种不确定性 知了堂双十一预售 0.11元畅享三大权益 助你快速实现自我提升 突破成长瓶颈 https://appyqk1x…

1. 网络之网络通信基础

网络通信基础 文章目录 网络通信基础1. IP地址2. 端口号3. 协议3.1 三要素3.2 作用 4. 五元组5. 协议分层5.1 OSI七层模型5.2 TCP/IP 五层模型5.2.1 应用层5.2.2 传输层5.2.3 网络层5.2.3 数据链路层5.2.5 物理层 6. 封装和分用6.1 发送方 - 封装6.2 中间转发6.3 接收方 - 分用…

codeMirror代码编辑器,如何定位并在编辑区域输入内容

背景 最近在写UI自动化&#xff0c;发现普通的方法不能在CodeMirror编辑器里面输入内容&#xff0c;只能通过JS的方式输入内容。 于是琢磨了一下selenium和playwright这2种自动化工具&#xff0c;在CodeMirror编辑器里面输入内容的差别。 注意&#xff1a;这里在定位CodeMirr…

轧钢厂安全生产方案:AI视频识别安全风险智能监管平台的设计

一、背景与需求 轧钢厂一般都使用打包机对线材进行打包作业&#xff0c;由于生产需要&#xff0c;人员需频繁进入打包机内作业&#xff0c;如&#xff1a;加护垫、整包、打包机检修、调试等作业。在轧钢厂生产过程中&#xff0c;每个班次生产线材超过300件&#xff0c;人员在一…

【OpenCV实现图像找到轮廓的不同特征,就像面积,周长,质心,边界框等等。】

文章目录 概要图像矩凸包边界矩形 概要 OpenCV是一个流行的计算机视觉库&#xff0c;它提供了许多图像处理和分析功能&#xff0c;其中包括查找图像中物体的轮廓。通过查找轮廓&#xff0c;可以提取许多有用的特征&#xff0c;如面积、周长、质心、边界框等。 以下是几种使用…

双目视觉检测 KX02-SY1000型测宽仪 有效修正和消除距离变化对测量的影响

双目视觉检测的基本原理 利用相机测量宽度时&#xff0c;由于单个相机在成像时存在“近大远小”的现象&#xff0c;并且单靠摄入的图像无法知道被测物的距离&#xff0c;所以由被测物的跳动导致的被测物到工业相机之间距离变化&#xff0c;使测量精度难以提高。 因此测宽仪需…

Vue项目创建与启动(2023超详细的图文教程)

目录 一、下载node.js 二、下载vue-cli与webpack插件 三、项目初始化(项目配置详细信息) 四、项目启动 五、Vue项目工程结构&#xff08;扩展知识&#xff09; 一、下载node.js 1.检测是否已经安装过node.js 打开控制台,输入 npm -v如果有会显示对应版本 如果没有会显示…

RocketMQ消费者和队列对应关系

参考 RocketMQ 5.0 POP 消费模式探秘 https://www.cnblogs.com/alisystemsoftware/p/15535925.html 旧版本MQ结论 消费者应用和topic队列一对多的关系。 &#xff08;一个消费组consumer group里&#xff0c;一个消费者应用可以消费多个队列的消息。一个队列的消息只能被一个…

矩阵分块例子

有如下矩阵A和B 对A列分块, B行分块后结果如下 对A行分块, B列分块后结果如下