[Java] Hàm hoán vị trong Java – Swap in Java

Việc thực hiện hàm swap (đổi chỗ – hoán vị) trong java nó hơi khác so với C/C++ hay Pascal,… Muốn thay đổi giá trị của các biến khi ra khỏi hàm thì bạn phải sử dụng đến các đối tượng. Các bạn có thể tham khảo 2 VD dưới đây cho hàm swap sau đó nếu muốn hãy đọc chi tiết ở bên dưới hoặc nếu đọc code thấy khó hiểu thì hãy đọc phần giải thích bên dưới trước rồi hãy quay trở về code.

VD1: Sử dụng đối tượng

package VietSource.net.temp;

import java.util.Scanner;

class Number {				// lop Number
	int num;

	public Number(int num) {		// ham khoi tao
		this.num = num;
	}

	public int getNum() {			// ham tra ve gia tri num
		return num;
	}

	public void setNum(int num) {	// ham set gia tri num
		this.num = num;
	}
}

public class MySwap {
	private void swap(Number a, Number b) {		// ham hoan vi
		int temp = a.getNum();					// gan num cua a cho temp
		a.setNum(b.getNum());					// gan num cua b cho a
		b.setNum(temp);							// gan temp cho num cua b
	}

	public static void main(String[] args) {
		Number a, b;								// hai doi tuong Number a va b
		MySwap sw = new MySwap();					// doi tuong sw thuoc lop MySwap
		Scanner scan = new Scanner(System.in);

		System.out.print("Nhap a = ");
		a = new Number(scan.nextInt()); 			// khoi tao a voi num la so nhap vao
		System.out.print("Nhap b = ");
		b = new Number(scan.nextInt()); 			// khoi tao b voi num la so nhap vao
		scan.close();
		System.out.println("Befor swap: a = " + a.getNum() + " b = " + b.getNum());
		sw.swap(a, b);								// su dung ham swap
		System.out.println("After swap: a = " + a.getNum() + " b = " + b.getNum());
	}
}

VD 2 Sử dụng qua mảng (thực chất mảng cũng là đối tượng)

package VietSource.net.temp;

import java.util.Scanner;

public class SwapArray {
	private void swapArray(int arr[], int i, int j){
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	
	public static void main(String[] args) {
		SwapArray sw = new SwapArray();
		Scanner scan = new Scanner(System.in);
		int arr[] = new int[2];
		
		System.out.print("Nhap a = ");
		arr[0] = scan.nextInt();
		System.out.print("Nhap b = ");
		arr[1] = scan.nextInt(); 
		
		System.out.println("Befor swap: a = " + arr[0] + " b = " + arr[1]);
		sw.swapArray(arr, 0, 1);
		System.out.println("After swap: a = " + arr[0] + " b = " + arr[1]);
		
		scan.close();
	}
}

Một trong những điểm khó khăn (và gây tranh cãi) cho những lập trình viên C++ khi lập trình Java là việc pass-by-value hay
pass-by-reference. Ví dụ:

Object a = new Object("Object A"); //kỳ quặc với C++: Object* a = new ...
Object b = a;
b.setNewName("Object B");

Theo cách hiểu của C++ devs thì rõ ràng Object b thay đổi tên chẳng liên quan gì đến Object a cả. Thực tế theo Java thì khác, Object a sẽ có name là “Object B” chẳng khác gì Object b cả. Để Object b hoàn toàn “độc lập” với Object a thì: Object b = a.clone();

Thế nhưng hàm swap() trong Java lại chẳng hoạt động theo cách pass-by-reference.

private void swap(Type arg1, Type arg2) {
	Type temp = arg1;
	arg1 = arg2;
	arg2 = temp;
}

Vậy đấy!!! Java thực sự đã làm như thế nào? Ẩn sau những cái lằng nhằng gây tranh cãi như vậy là gì?

Trước hết cần hiểu rằng pass-by-value là cách truyền bằng cách copy giá trị của tham số vào một “biến” được sử dụng trong nội hàm đó. Do đó khi hàm trả về thì sẽ hủy đi các tham số copied, và các tham số truyền vào thì không bị ảnh hưởng thay đổi gì. Trái ngược với nó là pass-by-reference thì thực sự tạo ra một alias tới các tham số thực sự. Khi trong hàm thay đổi các tham số đó thì nó cũng thay đổi trực tiếp các tham số truyền vào. Việc không phải tạo một bản copy ở trong C++ nhiều khi tiết kiệm được CPU với object lớn, có quá trình copy/khởi tạo phức tạp.
Tuy nhiên thì ở trong Java hàm swap không hoạt động vì nó hoạt động theo cách thức pass-by-value. Sau khi hàm swap trả về thì arg1 và arg2 không đổi chỗ cho nhau. Nhưng kỳ lạ hơn nếu trong thân hàm swap gọi một phương thức của Type làm thay đổi object, ví dụ như: arg1.setValue(“Change”);

Theo cách nghĩ pass-by-value thì sau khi thoát khỏi hàm swap thì arg1 chẳng bị thay đổi gì. Thực tế lại khác, arg1 đã gọi phương thức setValue() do đó khi thoát khỏi hàm thì value của arg1 đã thay đổi. Hãy nhớ lại rằng trong C cũng không có khái niệm reference (&). Thế nhưng C lại có pointer (con trỏ) và nó làm cho C mạnh mẽ uyển chuyển không cần có reference. Java có pointer không? Nhiều người cho rằng: Object thì là pass-by-reference còn các kiểu primitive thì là pass-by-value (In Java, Objects are passed by reference, and primitives are passed by value.) Mệnh đề này chỉ đúng một nửa. Object không phải được pass-by-reference mà là object reference được pass-by-value. Chúng ta sẽ hiểu rõ ở câu trả lời:
Câu trả lời cho tất cả các sự kỳ quặc trên ở Java đó chính là Java đã “làm sạch” cú pháp pointer trong C. Java có pointer nhưng pointer được khai báo và sử dụng một cách rất clean. Và tất nhiên nó tuy không mạnh bằng pointer trong C nhưng nó cũng “fix” tất cả những lỗi tiềm tàng và nguy hiểm (casting, releasing pointer hay pointer arithmetic) của pointer trong C.
Trong Java:
Object a;

giống như trong C/C++
Object* a;

Do đó khi
Object b = a;

có nghĩa trong C/C++
Object* b = a; // b và a đều là pointer


b.setName(“Object B”);

tương đương với
b->setName(“Object B”);

Thế nên b.setName(”Object B”) cũng đặt object “trỏ” bởi a có tên là “Object B” vì a và b trỏ cùng vào một object.

private void swap(Object a, Object b)
{
	Object temp = a;
	a = b;
	b = temp;
}

tương ứng với

void swap(Object* a, Object* b)
{
	Object* temp = a;
	a = b;
	b = temp;
}

Rõ ràng hàm này trong C không hoạt động trừ phi thay đổi thế này:

void swap(Object* a, Object* b)
{
	Object temp = *a;
	*a = *b;
	*b = temp;
}

“Con trỏ” a và b được truyền theo kiểu pass-by-value (có thể hiểu rằng Java giống như C, tất cả các tham số được truyền theo kiểu pass-by-value) nên sau khi hàm swap trả về thì a, b không bị thay đổi hay phép gán a = b không có ý nghĩa. Thế nhưng a.setValue(”…”) thì đã thay đổi object trỏ bởi con trỏ.

Tham khảo tại:
http://javadude.com/articles/passbyvalue.htm
http://java.forumvi.net/t23-topic