java swap函数_Java 利用swap函数交换两个整型数据值

提到交换两个数a, b的值,一定可以想起很多方法,a = a+b;b = a - b; a = a - b;借助temp的方法等等,但是如果食用swap方法的话,这个问题可是很有研究的了,要求使用swap(Integer a, Integer b)签名,交换a,b的值

int a = 1;

int b = 2;

//before

System.out.println(“a = %s, b = %s”, a, b);

swap(a, b);

System.out.println(“a = %s, b = %s”, a, b);

由于java中对于非原生类型都说传递引用,而对于原生数据类型都说通过传递值的方式传递参数的,如果仅仅通过传递值的方式来交换a,b的值,在Java中是不可能实现的,所以在定义函数签名的时候使用了Integer的方式,通过传递引用。

下面我们就来一步步的揭开这个题目的面纱:

通过传统方法,使用临时变量的方式

public void swap(Integer a, Integer b) {

Integer temp = a;

a = b;

b = temp;

}

以上方法看着没有任何问题,借助一个临时变量来实现变量的交换,那么运行一下,结果并没有想象的那样,等于没有任何的交换,那这个是为什么呢,明明传递的是引用,而且交换的是引用?

首先我们理解下由int -> Integer这个过程,我们知道这个叫做自动装箱,是JDK自带的功能,那么这个自动装箱经历了什么?我们可以通过javap -verbose命令查看编译后的class文件,我们会发现,在自动装箱的过程中,并不少直接new Integer,而是通过Integer.valueOf()方法将数字转换成Integer

34: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

37: iload_2

38: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

41: invokestatic  #8                  // Method swap:(Ljava/lang/Integer;Ljava/lang/Integer;)V

那么Integer.valueOf

public static Integer valueOf(int i) {

if (i >= IntegerCache.low && i <= IntegerCache.high)

return IntegerCache.cache[i + (-IntegerCache.low)];

return new Integer(i);

}

从IntegerCache中取出的,也就是说从缓冲中去的的值,而函数中的赋值操作,等于将i1->a 变成i1->b,将i2->b变成i1->a,这个和a,b本身没有任何关系,所以并没有成功交换两个数的值

借助反射来实现

那么这样直接赋值的方式无法改变两个数的值,那么我们可以猜想到,我们可以使用反射,而在Integer中存储值的是

private final int value;

如果通过反射的方式改变这个引用的值,应是可用行的,那么我们就尝试这种方法

public static void swap(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {

Field field = Integer.class.getDeclaredField("value");

field.setAccessible(true);

int temp = i1.intValue();

field.set(i1, i2.intValue());

field.set(i2, temp);

}

然后不幸的是,我们居然得到这样离奇的结果

before: a = 1, b = 2

after: a = 2, b = 2

这个是为什么呢,首先我们将i1的值赋值给了temp = 1,再将i2.intValue()的值赋值给了i1 = 2;,也就是说我吗将2赋值给了Integer.value(1)的这个实例,然后再将temp = 1赋值给i2,这里注意set函数的签名

public void set(Object obj, Object value)

第二个参数接受的是一个对象,而我们传递的是temp,那么就要进行自动装箱成Integer.value(1),而field.set(i1, i2.intValue());步,已经将Integer.valueOf(1)的值赋值为2,所有在field.set(i2, temp);就是将2赋值给了i2,就有了上面的输出结果。

再次借助反射

反射还是不行,主要是引用将temp自动装箱成了Integer.valueOf(1),而这个和i1使用了同一个引用,而直接使用了i1的值,那么如果不让temp自动装箱成Integer.valueOf(1)是不是就解决问题了,于是有了第三种方法:

public static void swap(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {

Field field = Integer.class.getDeclaredField("value");

field.setAccessible(true);

Integer temp = new Integer(i1.intValue());

field.set(i1, i2.intValue());

field.set(i2, temp);

}

这时我们得到一个正确的结果输出:

before: a = 1, b = 2

after: a = 2, b = 1

大功告成,终于将两个整数的值进行的交换

这时别高兴的太早,上面提到了一个问题,我们将Integer.value(1)中的值赋值成了2,也就是说Integer.valueOf(1) == 2将返回true,这样对整个系统都造成了影响,这样的方法也是有缺陷的,使用时要谨慎

通过Integer的setInt代替set方法

我们还有一种方式,可以避免自动装箱,Integer有一个setInt方法,签名为

public void setInt(Object obj, int i)

由于第二个参数是整型,也就避免了自动装箱,这时的实现代码为

public static void swap(Integer i1, Integer i2) throws NoSuchFieldException, IllegalAccessException {

Field field = Integer.class.getDeclaredField("value");

field.setAccessible(true);

int temp = i1.intValue();

field.setInt(i1, i2.intValue());

field.setInt(i2, temp);

}

同样这个方法也有上述方法3种的缺陷

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值