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

背景:我想校验一个指定的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;elsereturn 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.hqwc.cn/news/160775.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新版onenet平台安全鉴权的确定与使用

根据onenet官方更新的文档&#xff1a;平台提供开放的API接口&#xff0c;用户可以通过HTTP/HTTPS调用&#xff0c;进行设备管理&#xff0c;数据查询&#xff0c;设备命令交互等操作&#xff0c;在API的基础上&#xff0c;根据自己的个性化需求搭建上层应用。 为提高API访问安…

HTML和CSS的基础-前端扫盲

想要写出一个网页&#xff0c;就需要学习前端开发&#xff08;写网页代码&#xff09;和后端开发&#xff08;服务器代码&#xff09;。 对于前端的要求&#xff0c;我们不需要了解很深&#xff0c;仅仅需要做到扫盲的程度就可以了。 写前端&#xff0c;主要用到的有&#xf…

Vue H5页面长按保存为图片

安装依赖&#xff1a;npm install html2canvas -d <template><div class"index"><div id"captureId" class"capture" v-show"firstFlag"><ul><li>1</li><li>2</li><li>3<…

每日一题 --- 力扣2003—每棵子树内缺失的最小基因值

图片借用B站灵茶山文艾府 打卡代码&#xff08;记得看&#xff0c;有注释&#xff09;&#xff1a; class Solution { public:vector<int> smallestMissingValueSubtree(vector<int> &parents, vector<int> &nums) {int n parents.size();vector&l…

【漏洞复现】Drupal_小于7.32版本 _“Drupalgeddon” SQL注入漏洞(CVE-2014-3704)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 说明内容漏洞编号CVE-2014-3704漏洞名称Drupal “Drupalgeddon” SQL注入漏洞漏洞…

Technology strategy Pattern 学习笔记1-Context: Architecture and Strategy

Context: Architecture and Strategy 1 Architect and Strategist 1.1 three primary concerns of the architect 1.1.1 Contain entropy(熵-混乱程度&#xff0c;不确定性&#xff0c;惊奇程度&#xff0c;不可预测性&#xff0c;信息量等等&#xff09; The architect wh…

基于51单片机的烟雾和温湿度检测控制系统仿真(智能防火系统,火灾报警灭火系统)

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;单片机防火 获取完整源码源文件仿真源文件论文报告说明文档等 基于51单片机的光照及温湿度检测报警控制系统 由STC89C52单片机LCD1602液晶显示屏ADC0832模块蜂鸣器DHT11温湿度传感器 烟雾传感器LED按键构成 具体功能&…

java网络通信

浏览器中输入&#xff1a;“www.woaijava.com”之后都发生了什么&#xff1f; 请详细阐述 由域名→IP地址 寻找IP地址的过程依次经过了浏览器缓存、系统缓存、hosts文件、路由器缓存、 递归搜索根域名服务器。 建立TCP/IP连接&#xff08;三次握手具体过程&#xff09; 由浏览…

【JavaEE】JVM 剖析

JVM 1. JVM 的内存划分2. JVM 类加载机制2.1 类加载的大致流程2.2 双亲委派模型2.3 类加载的时机 3. 垃圾回收机制3.1 为什么会存在垃圾回收机制?3.2 垃圾回收, 到底实在做什么?3.3 垃圾回收的两步骤第一步: 判断对象是否是"垃圾"第二步: 如何回收垃圾 1. JVM 的内…

【网络协议】聊聊DNS协议如何域名解析和负载均衡

DNS 服务器 我们知道如果使用IP地址进行访问网站&#xff0c;很难进行记忆&#xff0c;所以DNS的作用是将域名转换成对应的IP地址。如果全世界都使用同一台DNS服务器&#xff0c;那么DNS服务器本身需要保证服务的高可用、高性能&#xff0c;以及分布式等。最好的方式就是分层。…

安吉寻梦桃花原

安吉——西湖边的那片竹海 安吉县&#xff0c;地处浙江西北部&#xff0c;湖州市辖县之一&#xff0c;北靠天目山&#xff0c;面向沪宁杭。建县于公元185年&#xff0c;县名出自《诗经》“安且吉兮”之意。 安吉县生态环境优美宜居&#xff0c;境内“七山一水二分田”&#xf…

el-select 搜索无选项时 请求接口添加输入的值

el-select 搜索无选项时 请求接口添加输入的值 <template><div class"flex"><el-select class"w250" v-model"state.brand.id" placeholder"请选择" clearable filterable :filter-method"handleQu…