Thinking logic of computer programs (27), thinking 27

Source: Internet
Author: User

Thinking logic of computer programs (27), thinking 27

In this section, we will continue to discuss the packaging class, mainly the Integer class. Next we will introduce the Character class. Long is similar to Integer, so we will not discuss it separately. Other classes are basically introduced and will not be described again.

What else do I need to introduce to a simple Integer? It has some binary operations. Let's take a look. In addition, we also analyze its valueOf implementation.

Why do we need to care about the implementation code? In most cases, we do not need to worry about it. We can use it to learn, especially binary operations. Binary is the foundation of computers, but the code is often obscure and we hope to have a clearer and deeper understanding of it.

Let's first look at the flip by bit.

Bit flip

Usage

Integer has two static methods, which can be flipped by bit:

public static int reverse(int i)public static int reverseBytes(int i)

Bit flip refers to the use of int as binary. The bit on the left is exchanged with the bit on the right. The reverse is exchanged by bit, And the reverseBytes is exchanged by byte. Let's look at an example:

int a = 0x12345678;System.out.println(Integer.toBinaryString(a));int r = Integer.reverse(a);System.out.println(Integer.toBinaryString(r));int rb = Integer.reverseBytes(a);System.out.println(Integer.toHexString(rb));

A is an integer. It is assigned a value in hexadecimal notation. First, it outputs its binary string, then the binary value after reverse, and finally the hexadecimal value of reverseBytes. The output is:

100100011010001010110011110001111001101010001011000100100078563412

ReverseBytes is flipped by byte, 78 is a byte in hexadecimal notation, and 12 is also, so result 78563412 is easier to understand.

Binary flip is incorrect at first, because the output is not 32 bits, and the previous 0 is ignored during the output. let's complete the 32 bits and then look at it:

0001001000110100010101100111100000011110011010100010110001001000

The result is correct.

How are these two methods implemented?

ReverseBytes

Let's look at the reverseBytes code:

public static int reverseBytes(int i) {    return ((i >>> 24)           ) |           ((i >>   8) &   0xFF00) |           ((i <<   8) & 0xFF0000) |           ((i << 24));}

Take the parameter I = 0x12345678 as an example to analyze the execution process:

I >>> 24 unsigned right shift. The maximum byte value is 0x00000012.

(I> 8) & 0xFF00, the second byte on the left is moved to the second byte on the right. I> 8, the result is 0x00123456, and then & 0xFF00, the second byte on the right is retained and the result is 0x00003400.

(I <8) & 0xFF0000, the second byte on the right is moved to the second byte on the left, I <8 returns 0x34567800, and then & 0xFF0000, the third byte on the right is retained and the result is 0x00560000.

I <24, the result is 0x78000000, And the rightmost byte is moved to the leftmost.

These four results are then performed or operated | the result is 0x78563412. In this way, byte flip is achieved through the left shift, right shift, and or operation.

Reverse

Let's take a look at the reverse code:

public static int reverse(int i) {    // HD, Figure 7-1    i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;    i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;    i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;    i = (i << 24) | ((i & 0xff00) << 8) |        ((i >>> 8) & 0xff00) | (i >>> 24);    return i;}

Although this code is short, it is very obscure. What does it mean?

The first line of the Code is a comment, "HD, Figure 7-1". What does this mean? HD represents a book named Hacker's Delight, and HD stands for it. Figure 7-1 is the Figure 7-1 in the book. Related content is shown in this book:

We can see that the reverse code in Integer is to copy the 7-1 code in this book. The explanation of this Code is also illustrated in the figure. Let's translate it.

The basic idea of efficient bit flip is to first exchange adjacent single bit, then take two as a group, and then exchange adjacent bit, then there is a group of four digits, and then there are eight or Sixteen digits. The 16 digits are complete. This idea is not only applicable to binary, but also to decimal. For ease of understanding, let's look at a decimal example, for example, to flip the number 12345678,

In the first round, a single adjacent number is exchanged and the result is:

21 43 65 87

In the second round, two numbers are used as a group to exchange adjacent values. The result is:

43 21 87 65

In the third round, a group of four numbers is exchanged and the result is:

8765 4321

Flip complete.

This is not efficient in decimal format, but it is efficient in binary format because binary can exchange multiple adjacent bits in one command.

This line of code is used to swap adjacent single bits:

x = (x & 0x55555555) <<  1 | (x & 0xAAAAAAAA) >>>  1;

The binary value of 5 is 0101, And the binary value of 0x55555555 is:

01010101010101010101010101010101

X & 0x55555555 is the odd number of x.

The binary value of A is 1010, And the binary value of 0xAAAAAAAA is:

10101010101010101010101010101010

X & 0xAAAAAAAA is an even number of digits of x.

(x & 0x55555555) <<  1 | (x & 0xAAAAAAAA) >>>  1;

This indicates that the odd digits of x are shifted to the left, even digits are shifted to the right, and then | is used to merge to swap adjacent digits. This code can have a small optimization. Only a constant 0x55555555 is used, and the second half is first shifted before performing the operation, which is changed:

(i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;

Similarly, the following code uses two digits as a group to swap adjacent bits:

i = (i & 0x33333333) << 2 | (i & 0xCCCCCCCC)>>>2;

The binary value of 3 is 0011, And the binary value of 0x33333333 is:

00110011001100110011001100110011 

X & 0x33333333 is the lower half of x in a group of two digits.

The binary value of C is 1100, And the binary value of 0xCCCCCCCC is:

11001100110011001100110011001100

X & 0xCCCCCCCC is the high half of x in a group of two digits.

(i & 0x33333333) << 2 | (i & 0xCCCCCCCC)>>>2;

It indicates that x takes two parts as a group, the lower half part is in the high displacement, the higher half is in the low displacement, and then merges through | to achieve the purpose of exchange. Similarly, the constant 0 xCCCCCCCC can be removed and the code can be optimized:

(i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;

Similarly, the following code is to exchange a group of four digits.

i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;

When it is switched to eight bits, the bytes are flipped and can be written in the following more direct form. The code is basically the same as the reverseBytes.

i = (i << 24) | ((i & 0xff00) << 8) |    ((i >>> 8) & 0xff00) | (i >>> 24);

Why is the reverse code so obscure? Or cannot it be written in a more understandable way? For example, to implement flip, a common idea is that the first and last exchanges, the second and last exchanges until the two exchanges are completed. If the data is not a binary bit, this approach is good, but for Binary bit, this efficiency is relatively low.

CPU commands cannot operate on a single bit efficiently. Generally, the minimum data unit is 32 bits (32 bits). In addition, the CPU can efficiently implement shift and logical operations, however, addition, subtraction, multiplication, division are slow.

Reverse fully utilizes these features of the CPU to exchange adjacent bits in parallel and efficiently. It can also implement the same function in other methods that are easier to understand, but it is hard to be more efficient than this code.

Cyclic shift

Usage

Integer has two static methods for cyclic shift:

public static int rotateLeft(int i, int distance)public static int rotateRight(int i, int distance) 

RotateLeft shifts the cycle to the left, rotateRight shifts the right of the loop, and distance shifts the number of digits. The so-called cyclic shift is relative to the normal shift, for example, moving the left two places, the original maximum two will be gone, and the right side will be filled with 0. If the cycle is shifted to the left, the original maximum two will be moved to the rightmost, just like a ring connected between left and right. Let's look at an example:

int a = 0x12345678;int b = Integer.rotateLeft(a, 8);System.out.println(Integer.toHexString(b));int c = Integer.rotateRight(a, 8);System.out.println(Integer.toHexString(c))

B is the result of shifts 8 bits to the left of a loop, and c is the result of shifts 8 bits to the right of a loop, so the output is:

3456781278123456

Implementation Code

The implementation code of these two functions is:

public static int rotateLeft(int i, int distance) {    return (i << distance) | (i >>> -distance);}public static int rotateRight(int i, int distance) {    return (i >>> distance) | (i << -distance);}

The two functions are confusing negative numbers. If distance is 8, what does I >>>-8 mean? In fact, the actual number of shifts is not the direct number next to it, but the minimum 5-digit value of the direct number, or the result of the direct number & 0x1f. This is because the maximum value of 5 bits is 31, and the 31 bits are not valid for the int integer.

After understanding the meaning of moving negative digits, we can easily use the above Code. For example, the binary representation of-8 is:

11111111111111111111111111111000

The minimum 5-digit is 11000, And the decimal value is 24. Therefore, I >>>-8 is I >>> 24, I <8 | I> 24 means that the cycle shifts eight places to the left.

In the code above, I >>>-distance is I >>> (32-distance), And I <-distance is I <(32-distance ).

Search and count by bit

There are other bitwise operations in Integer, including:

public static int signum(int i)

View the symbol bit. If positive, 1 is returned. If negative,-is returned. If 0 is returned, 0 is returned.

public static int lowestOneBit(int i)

Find the position from the first 1 on the right. This bit remains unchanged. If the other bit is set to 0, this integer is returned. For example, for 3, the binary value is 11, the binary value is 01, and the decimal value is 1. For 20, the binary value is 10100, the result is 00100, And the decimal value is 4.

public static int highestOneBit(int i) 

Find the first position of 1 on the left. The value remains unchanged. If the other bits are set to 0, this integer is returned.

public static int bitCount(int i)  

Returns the number of 1 in binary. For example, 20, the binary value is, and the number of 1 is 2.

public static int numberOfLeadingZeros(int i)

The number of consecutive numbers starting with 0 on the left. For example, 20 is binary 10100, and 27 are 0 on the left.

public static int numberOfTrailingZeros(int i)

Number of consecutive 0 at the end of the right side. For example, if the binary value is 10100, there are two zeros on the right.

For its implementation code, there are comments pointing to the relevant chapter of Hacker's Delight. This article will not go into detail.

Implementation of valueOf

As mentioned above, when creating a packaging class object, you can use the static valueOf method or new method, but we recommend using valueOf. Why? Let's look at the valueOf code:

public static Integer valueOf(int i) {    assert IntegerCache.high >= 127;    if (i >= IntegerCache.low && i <= IntegerCache.high)        return IntegerCache.cache[i + (-IntegerCache.low)];    return new Integer(i);}

It uses IntegerCache, which is a private static internal class, the Code is as follows:

private static class IntegerCache {    static final int low = -128;    static final int high;    static final Integer cache[];    static {        // high value may be configured by property        int h = 127;        String integerCacheHighPropValue =            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");        if (integerCacheHighPropValue != null) {            int i = parseInt(integerCacheHighPropValue);            i = Math.max(i, 127);            // Maximum array size is Integer.MAX_VALUE            h = Math.min(i, Integer.MAX_VALUE - (-low) -1);        }        high = h;        cache = new Integer[(high - low) + 1];        int j = low;        for(int k = 0; k < cache.length; k++)            cache[k] = new Integer(j++);    }    private IntegerCache() {}}

IntegerCache indicates the Integer cache. The cache variable is a static Integer array and is initialized in the static initialization code block. By default, it stores data from-128 to 127, A total of 256 integers correspond to Integer objects.

In the valueOf code, if the value is in the cached range, that is, the default value is-128 to 127, you can directly obtain the pre-created Integer object from IntegerCache, to create an object through new.

By sharing common objects, you can save memory space. Because Integer is immutable, cached objects can be securely shared. Boolean/Byte/Short/Long/Character all have similar implementations. This idea of sharing common objects is a common design concept. In the book <design mode>, it is assigned a name called the "enjoy" model, which is called "Flyweight" in English, that is, shared lightweight elements.

Summary

This section describes some bitwise operations in Integer. Bit operation code is obscure, but the performance is relatively high. We have explained some of the Code in detail. If you want to learn more, you can view the Hacker's Delight book based on the notes. We also introduced the implementation of valueOf and the metadata sharing mode.

Next, let's discuss Character.

----------------

For more information, see the latest article. Please pay attention to the Public Account "lauma says programming" (scan the QR code below), from entry to advanced, ma and you explore the essence of Java programming and computer technology. Write with your heart, original articles, and retain all copyrights.

-----------

More original articles

Thinking logic of computer programs (1)-data and variables

Thinking logic of computer programs (5)-Why is an error in decimal calculation?

Thinking logic of computer programs (6)-How to recover from garbled code (I )?

Thinking logic of computer programs (8)-true meaning of char

Thinking logic of computer programs (12)-Basic Principles of function calls

Thinking logic of computer programs (17)-Basic Principle of inheritance implementation

Thinking logic of computer programs (18)-Why inheritance is a double-edged sword

Thinking logic of computer programs (19)-essence of interfaces

Thinking logic of computer programs (20)-Why abstract classes?

Thinking logic of computer programs (21)-nature of internal classes

Thinking logic of computer programs (23)-nature of enumeration

Thinking logic of computer programs (24)-exception (I)

Thinking logic of computer programs (25)-exception (lower)

Thinking logic of computer programs (26)-analytical packaging (I)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.