浅谈java两个数的异或交换以及Integer的缓存机制
今天看数据结构的时候看到一个通过异或(位运算)交换数组内的数的方法。
import java.lang.reflect.Field;
public class test {
public static void main(String[] args) {
int arr[]={1,2,3,4};
System.out.println("1:"+arr[0]+"|2:"+arr[3]);
swap(arr, 0, 3);
System.out.println("1:"+arr[0]+"|2:"+arr[3]);
}
public static void swap(int []arr, int i, int j) {
//i=2 j=3
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
/*
流程分析如何实现交换
0101 = 0001 ^ 0100 = 6
0001 = 0101 ^ 0100 = 1
0100 = 0101 ^ 0001 = 5
**/
}
}
在尝试之后,联想到了java没有指针,java做不到真正的引用传递(对一块内存地址操作,而非对拷贝的地址),便想到用反射交换两个数
import java.lang.reflect.Field;
public class test {
public static void main(String[] args) {
Integer a =2, b=3;
System.out.println("a="+a+" | b="+b);
try {
swap2(a, b);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("a="+a+" | b="+b);
}
public static void swap2(Integer a, Integer b) throws NoSuchFieldException, IllegalAccessException {
int temp = a;
Field field = a.getClass().getDeclaredField("value");
field.setAccessible(true);
field.setInt(a, b);
field.setInt(b, temp);
}
}
运行截图
而如果将int temp = a;改为Integer temp = a;呢
结果如下
这是什么原因呢?
这就涉及到了java中Integer 的缓存机制
Integer的缓存机制: Integer是对小数据(-128~127)是有缓存的,再jvm初始化的时候,数据-128~127之间的数字便被缓存到了本地内存中,如果初始化-128~127之间的数字,会直接从内存中取出,不需要新建一个对象.
我们添加一个变量debug一下
下一步
wow,很有意思,temp的值’貌似‘也随着a的变化而变化了,但事实是这样吗?下一行的temp2怎么也变化了?它只是做了一个简单的赋值,它怎么也变了?
个人理解:当缓存中存在某个值a时,如果存在多个Integer对象的值都等于(或指向)这个缓存值a,那么当a改变了值的时候,原本等于(指向)a的Integer对象的值也会随着改变,由于Integer是对小数据(-128~127)是有缓存的,上述代码a和temp2都等于2 符合条件,因此temp2也会随着改变
那么当我们把a和temp的值改为128时
下一步
如我们所料,这次temp2的值并没有改变。
很好,我们来看一下Integer的源码就能更好地理解了
public final class Integer extends Number implements Comparable {
//...省略...
/*
初始化一个Integer数组——cache,最小值 -128,最大值默认为 127,但可以从属性
"java.lang.Integer.IntegerCache.high"中获取设置的值从而修改最大值
*/
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) {
try {
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);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
//...省略...
}
那么如何修改这个初始化的最大值呢?我们以IDEA为例
找到VM options并添加命令
-Djava.lang.Integer.IntegerCache.high={你想要设置的上限}
测试
ok,Integer的缓存的最大值已经被改变了。
那好,我们回到开始,如何用Integer定义的变量实现交换而不是随着a的变化而变化呢?
改成下面这样就可以了
Integer temp = new Integer(a.intValue());
那为什么这次的temp不会随着a的值变化而变化呢?因为new和直接赋值并不一样
直接赋值的Integer变量指向的是 java 常量池中的对象(也就是在缓存中的数据)
而new Integer() 的变量指向堆中新建的对象,两者在内存中的地址不同。
所以你明白了吧。
一开始也没想到简单的swap能扯到这么多东西,不过感觉到了知识之间的联系,o( ̄▽ ̄)d
如果看到哪里有问题或者描述不准确的话,请多多指教啊。