Java位运算—高级用法与深入解读(上)

简介: Java位运算—高级用法与深入解读(上)

一、开篇浅谈


开篇我们先来小谈一下:如果你是一名高级工程师或者是架构师,你在读源码的过程中,一定见过如下的代码,可以发现源码里运用了很多位运算来提高性能。


ArrayList.class 源码节选

ArrayList源码解读—Java8版本


    private void grow(int minCapacity) {
        ...
        //ArrayList扩容1.5倍关键代码
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        ...
    }



LinkedList.class 源码节选

LinkedList源码解读—Java8版本

  //通过判断索引靠链表的前面还是后面,提高效率
    Node<E> node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) {
            ...
        } else {
            ...
        }
    }


HashMap.class 源码节选

HashMap源码解读-Java8版本

   // 获取一个既大于 cap 又最接近 cap 的 2 的整数次幂数值
   static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }


二、基本概念


在开始java位运算的知识之前,我们先来了解几个基础的概念,机器数,真值,原码,反码,补码。


1.机器数

我们知道无论是代码还是数值,在计算机中最后都转换成以二进制的形式存在的,而一个数值在计算机中的二进制表示形式,就是这个数的机器数。机器数是有符号位的,在计算机中用一个二进制数的最高位存放符号,正数为0,负数为1,如下实例(按原码表示):


十进制的+5,计算机字长为8位,其二进制就是00000101


十进制的-5,计算机字长为8位,其二进制就是10000101(这里用的是原码)


其中00000101和10000101就是机器数


2.真值


由于机器数的第一位是符号位,所以其形式值就不等于其真值的数值,也就是说10000101表示的是-5而不是133(10000101的十进制是131,前提是不算最高位为符号位),因此-5才是机器数的真值。


3.原码


原码是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1,其余位表示数值的大小。


[+5]=[00000101](原码)


[ - 5]=[10000101](原码)


因为第一位是符号位,因此8位二进制的取值范围就是[1111 1111,0111 1111]也就是[-127,127]


4.反码


反码是数值存储的一种,但是由于补码更能有效表现数字在计算机中的形式,所以多数计算机一般都不采用反码表示数,反码的表示方法如下:


正数的反码是其本身


负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.


[+5]=[00000101](原码)= [00000101](反码)


[ - 5]=[10000101](原码)= [11111010](反码)


5.补码


在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。补码的表示方法是:


正数的补码就是其本身


负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)


[+5]=[00000101](原码)= [00000101](反码)=[00000101](补码)


[ - 5]=[10000101](原码)= [11111010](反码)=[11111011](补码)


6.补充


计算机中的符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。而在计算机系统中,数值一律用补码来表示和存储。


三、Java位运算


位移操作:(只针对 int类型的数据有效,java中,一个int的长度始终是32位,也就是4个字节,它操作的都是该整数的二进制数).也可作用于以下类型,即 byte,short,char,long(它们都是整数形式)。当为这四种类型时,JVM先把它们转换成int型再进行操作。


7.左移(<<)


m<<n的含义:把整数m表示的二进制数左移n位,高位移出n位都舍弃,低位补0. (此时将会出现正数变成负数的可能),如下实例:


5<<2 :把十进制的数值5左移两位,按如下步骤计算,


把5转位16位的二进制机器数:00000000 00000000 00000000 00000101


按左移原理,将二进制数左移两位:00000000 00000000 00000000 00010100


左移后结果为20


5<<29:把十进制的数值5左移29位,按如下步骤计算,


把5转位16位的二进制机器数:00000000 00000000 00000000 00000101


按左移原理,将二进制数左移29位:10100000 00000000 00000000 00000000


左移后高位是1,结果显然是负数


小结:m<<n即在数字没有溢出的前提下,对于正数和负数,左移n位都相当于m乘以2的n次方.


8.右移(>>)


m>>n的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1,实例如下:


5>>2 :把十进制的数值5右移两位,按如下步骤计算,


把5转位16位的二进制机器数:00000000 00000000 00000000 00000101


按右移原理,将二进制数左移两位:00000000 00000000 00000000 00000001


右移后结果为1


-5>>2:把十进制的数值-5右移两位,按如下步骤计算,


把-5转位16位的二进制机器数:11111111 11111111 11111111 11111011


按右移原理,将二进制数右移两位:11111111 11111111 11111111 11111110


右移后结果为-2


小结: m>>n即相当于m除以2的n次方,得到的为整数时,即为结果。如果结果为小数,此时会出现两种情况:


如果m为正数,得到的商会无条件 的舍弃小数位;


如果m为负数,舍弃小数部分,然后把整数部分加+1得到位移后的值。


相关文章
|
8月前
|
Java 数据安全/隐私保护 计算机视觉
Java位运算的详解
总的来说,Java位运算是一种强大的工具,它可以让我们直接操作整数的二进制位,实现各种复杂的功能。但是,位运算也是一把双刃剑,如果不正确地使用,可能会导致各种难以预料的问题。因此,使用位运算时,一定要小心谨慎,确保理解了每个运算的含义和效果。
233 24
|
12月前
|
存储 Java
Java中的位运算
本文介绍了位运算符的基础知识,包括原码、反码、补码的概念,以及常见的位运算符(如移位运算符 `&lt;&lt;`、`&gt;&gt;`、`&gt;&gt;&gt;` 和逻辑运算符 `&`、`|`、`^`、`~`)的使用方法和规则。通过具体的二进制示例,详细解释了这些运算符的工作原理,帮助读者更好地理解位运算在计算机中的应用。
178 2
Java中的位运算
|
12月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
10550 5
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
存储 安全 Java
深入理解Java中的FutureTask:用法和原理
【10月更文挑战第28天】`FutureTask` 是 Java 中 `java.util.concurrent` 包下的一个类,实现了 `RunnableFuture` 接口,支持异步计算和结果获取。它可以作为 `Runnable` 被线程执行,同时通过 `Future` 接口获取计算结果。`FutureTask` 可以基于 `Callable` 或 `Runnable` 创建,常用于多线程环境中执行耗时任务,避免阻塞主线程。任务结果可通过 `get` 方法获取,支持阻塞和非阻塞方式。内部使用 AQS 实现同步机制,确保线程安全。
1250 3
|
Java
Java 正则表达式高级用法
Java 中的正则表达式是强大的文本处理工具,用于搜索、匹配、替换和分割字符串。`java.util.regex` 包提供了 `Pattern` 和 `Matcher` 类来高效处理正则表达式。本文介绍了高级用法,包括使用 `Pattern` 和 `Matcher` 进行匹配、断言(如正向和负向前瞻/后顾)、捕获组与命名组、替换操作、分割字符串、修饰符(如忽略大小写和多行模式)及 Unicode 支持。通过这些功能,可以高效地处理复杂文本数据。
229 10
|
存储 Java 数据处理
Java 数组的高级用法
在 Java 中,数组不仅可以存储同类型的数据,还支持多种高级用法,如多维数组(常用于矩阵)、动态创建数组、克隆数组、使用 `java.util.Arrays` 进行排序和搜索、与集合相互转换、增强 for 循环遍历、匿名数组传递以及利用 `Arrays.equals()` 比较数组内容。这些技巧能提升代码的灵活性和可读性,适用于更复杂的数据处理场景。
137 10
|
安全 Java
Java switch case隐藏用法
在 Java 中,`switch` 语句是一种多分支选择结构,常用于根据变量值执行不同代码块。除基本用法外,它还有多种进阶技巧,如使用字符串(Java 7 开始支持)、多个 `case` 共享代码块、不使用 `break` 实现 “fall-through”、使用枚举类型、使用表达式(Java 12 及以上)、组合条件以及使用标签等。这些技巧使代码更加简洁、清晰且高效。
401 1
|
Java 数据处理
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
427 2
|
Java 开发者
Java中的并发编程:从基础到高级
在Java世界中,并发编程是一项至关重要的技能。本文将深入探讨Java并发编程的核心概念、实用工具和高级技术。我们将从线程基础出发,逐步过渡到线程池的使用,最后探索Java并发包中的强大工具,如CyclicBarrier、Semaphore和CountDownLatch。无论你是Java新手还是资深开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你在多线程环境中编写出更加高效、稳定的代码。 【7月更文挑战第30天】
129 7
|
Java 程序员 API
Java中的异常处理:从基础到高级
【7月更文挑战第28天】在Java编程的世界中,异常处理是一块基石,它确保了程序的健壮性和可靠性。本文将带领读者深入理解Java的异常处理机制,从基本的try-catch语句开始,逐步探索更复杂的异常处理策略,如finally块、自定义异常以及异常链。我们还会讨论如何在设计良好的API时利用异常处理来提高用户体验。通过这篇文章,读者将能够更加自信地处理各种异常情况,编写出更加稳定和用户友好的Java应用程序。