Toán tử trong Java

1. Toán tử trong Java

Toán tử (Operator) là một phương thức dùng để thao tác trên số học. Một toán tử được đặc trưng bởi tênký hiệu.

Toán tử trong Java cũng giống các toán tử trong các ngôn ngữ lập trình khác. Java có 8 kiểu dữ liệu nguyên thủy nhưng tổng quan thì nó chia làm nhóm lớn là logic và số học. Toán tử được sử dụng để tương tác đến các kiểu dữ liệu này.

Trong nhiều tài liệu lập trình Java, họ có cách chia nhóm toán tử khác nhau. Những chỉ khác rất nhỏ về quan điểm chứ số lượng toán tử vẫn vậy nó không hề mất đi hay tăng thêm. Theo tôi toán tử được chia làm 5 nhóm chính: Toán tử logic, Toán tử với bit, Toán tử với số học, Toán tử với quan hệ,
Toán tử giành cho gán
. Chỉ cần biết nhóm toán tử này gần như là ta đã tương tác gần hết đến các hành vi của máy tính. Nhỏ nhất là tương tác đến nhóm bit, logic. To nhất là tương tác đến số học. So sánh mối quan hệ các toán hạng ta có toán tử nhóm quan hệ.

2. Toán tử Bit

Bit là đơn vị nhỏ nhất trong máy tính. Nó gần giống như nguyên tử trong thế giới thực của chúng ta. Tất cả các số học, ký tự sau này điều được xây dựng dựa trên đây. Ta thấy trong các tài liệu đề cập kiểu dữ liệu điều nói đến độ lớn của nó bao nhiêu bit (thỉnh thoảng là bao nhiêu byte. 1 byte = 8 bit). Nếu cần rõ hơn ta cần đọc thêm trong phần kiểu dữ liệu trong Java. Do đó toán tử bit là thứ ta đề cập đầu tiên.

bit là “chất liệu” để cấu tạo nên các kiểu dữ liệu nên toán tử trên bit cũng được áp dụng cho tất cả kiểu dữ liệu nguyên thủy. Toán tử bit được chia làm 2 nhóm chính.

  1. Toán tử nhóm 1: ~ (NOT), & (AND), | (OR), ^ (XOR).
  2. Toán tử nhóm 2: << (dịch sang trái), >> (dịch sang phải)

Đối với nhóm 1, Ta giả sử ta đang có 1 bit với giá trị 1. Các toán tử điều là toán tử 2 ngôi riêng toán tử ~ (đảo bit) là toán tử 1 ngôi.

A B ~A A & B A | B A ^ B
0 0 1 0 0 0
1 0 0 0 1 1
0 1 1 0 1 1
1 1 0 1 1 0

Bảng  1 – Toán tử trên BIT.

Trên là vài ví dụ về 4 toán tử của bit. Toán tử chỉ thực hiện trên hai trạng thái true/ false hay nói cách khác là 1/ 0. Trong 4 toán tử này chỉ có ~ là toán tử có 1 toán hạng còn lại 3 toán tử còn lại điều là 2 toán hạng.

Đối với nhóm 2, được sử dụng khi ta có nhiều bit đang nằm liền kề nhau tạo thành một khối. Toán tử << giúp ta dịch chuyển các bit sang trái, >> giúp ta dịch chuyển các bit sang phải. Trong Java khi di chuyển bit, bit còn thiếu ở đầu/ cuối của khối bit sẽ được bù lại bằng bit 0.

Current a:  01111111
a>>1:  00111111     // move right 1 bit
a<<1:  11111110     // move left 1 bit</code></pre>

Trong đoạn mã trên a là khối bit hiện tại <<, >> di chuyển sang trái hay di chuyển sang phải và 1 là di chuyển 1 bit (nếu di chuyển 2 bit thì nó là 2…).

3. Toán tử Logic

Toán tử Logic là toán tử thể hiện rõ các toán tử của bitlogic chỉ là 1 bit. Logic là một định nghĩa mang tính đúng sai rõ ràng. Nó gắn với thế giới thực qua nhận biết của chúng ta. Toán tử logic thao tác trên 1 bit và chỉ chứa hai giá trị là 1 hoặc 0 đôi khi người ta gọi là true/ false.

Dù gọi gì đi nữa thì nó được hiểu là ha giá trị hoàn toàn trái ngược nhau. Nó giống như công tắc điện với hành vi tắt/ mở. Kiểu này tồn tại trong kiểu boolean của Java mà ta đã biết trong phần “Kiểu dữ liệu trong Java”.

Logic có 4 toán tử chính: && (and), || (or), ! (not equal), = (equal)

Với &&|| thì chúng ta có quy tắc dễ nhớ. AND chỉ đúng khi tất cả điều đúng. OR chỉ sai khi tất cả điều sai.

A B ! A A && B A || B
0 0 1 0 0
1 0 0 0 1
0 1 1 0 1
1 1 0 1 1

Bảng  2 – Toán tử logic dựa trên 2 toán hạng.

Đây là ví dụ trên mã:

*1 && 1 = 1
1 && 0 = 0
0 && 1 = 0
0 && 0 = 0 *
*1 || 1 = 1                 
1 || 0 = 1
0 || 1 = 1
0 || 0 = 0 *
*! 1 = 0
! 0 = 1 *

Đó là 4 toán tử chính trong logic. Trong Java các lập trình viên có thể viết mã ngắn ngọn bằng cách sử dụng toán tử logic với 3 ngôi. {điều kiện} ? {khi điều kiện đúng} : {khi điều kiện sai}. Toán tử này được đặc trung bằng cập dấu _?_:_.

int b = 1;
String s = b == 1 ? "one" : "none".

Giá trị của biến s là one vì b = 1.

4. Toán tử Số học

Toán tử số học được thực hiện trên toán hạng số. Trong Java có hai nhóm kiểu dữ liệu số nguyên thủy: số nguyên *và số thực.* Chúng sẽ là *toán hạng *trong các toán tử về số học.

  1. Toán tử số học nhóm 1: — (giảm), ++ (tăng)
  2. Toán tử số học nhóm 2: ** (nhân), / (chia), % (modulo) *
  3. Toán tử số học nhóm 3: + (cộng), – (trừ)

Trên là 3 nhóm của toán tử số học với độ ưu tiên từ trên xuống theo nhóm và từ trái sang phải nếu cùng nhóm. Ta cần lưu ý đến độ ưu tiên của toán hạng để biết phép toán nào tính trước, phép toán nào tính sau để ra kết quả đúng. Độ ưu tiên trong máy tính hoàn toán giống với toán học bên ngoài.

Đối với nhóm 1, toán tử giảm *và *tăng là 2 toán tử mỗi lần sẽ +/- 1. Ta cần lưu ý đối với toán hạng --, ++ vì chúng có thêm 2 khái niệm tiền tố (prefix) *và *hậu tố (postfix).

int a = 1, b;
b = ++a;    // Prefix
System.out.println("Print variable with prefix");
System.out.println(a);
System.out.println(b);

a = 1;
b = a++;    // Postfix
System.out.println("Print variable with postfix");
System.out.println(a);
System.out.println(b);
--- output
Print variable with prefix
2
2
Print variable with postfix
2
1

Ta tiền tố sẽ thực hiện phép toán +1 sau đó gán lại cho b. Nhưng hậu tố thực hiện phép gán cho b *trước sau đó mới *+1 cho a. Ta có thể nhớ bằng câu “Tiền-Gán, Gán-Hậu”.

Đối với nhóm 2, nhóm các toán tử bình thường và thườn thấy trong số học. Riêng % (modulo) là toán tử lấy phần dư sau khi thực hiện chia 2 toán hạng. Ta xét đoạn mã sau:

int a = 10;
long b = 10L;
float c = 10.0F;
double d = 10.0D;

System.out.println("\n -- int & long: 10 % 3");
System.out.println(a / 3);
System.out.println(b / 3);

System.out.println("\n -- float & double: 10 % 3");
System.out.println(c / 3);
System.out.println(d / 3);
--- output
-- int & long: 10 / 3
3
3

-- float & double: 10 / 3
3.3333333
3.3333333333333335

Đối với số nguyên 10/3 chỉ được 3 mà không có thêm phần dư 1. Nhưng đối với số thực thì lấy thêm phần dư phía sau dưới dạng thập phân. Trong ví dụ trên ta thỉnh thoảng tưởng nhằm là 10.0F / 3 = 3.333333 sẽ còn số dư rất nhỏ. Nhưng thực chất khi ta dùng % để lấy số dư cho 4 kiểu dữ liệu này điều là 1 hay 1.0.

System.out.println("\n -- Modulo: 10 % 3");
System.out.println(a % 3);
System.out.println(b % 3);
System.out.println(c % 3);
System.out.println(d % 3);
--- output
-- Modul: 10 % 3
1
1
1.0
1.0

Đối với nhóm 3, không có gì cần nói thêm vì đây là 2 phép toán cơ bản trong số học.

Khi biểu thức có 2 toán hạng có độ lớn khác nhau thì kết quả sẽ trả về với kiểu dữ liệu lớn nhất trong số chúng. Ví dụ: 10.0D / 2 = 5.0D (kết quả là 5.0 với kiểu double).

5. Toán tử Gán

Toán tử này được đặc trưng bởi dấu “=” và thuộc toán tử hai ngôi vì có hai toán hạng trong biểu thức. Nó được sử dụng để gán toán hạng bên phải cho toán hạng bên trái của nó.

int a = 1987;   // assigne variable a with value is 1987.

Toán tử gán đơn giản chỉ có dấu “=” như trên thực tế lập trình viên có thể kết hợp hai toán tử cùng thực hiện trên hai toán hạng đó. Sự kết hợp này ta thường thấy trong số học là +=, -=, *= , /=, %=. Cũng có thể kết hợp toán tử = với các toán tử của bit: <<=, >>=, &=, ^=, |=…

6. Toán tử Quan hệ

Toán tử quan hệ dùng để so sánh 2 toán hạng với nhau. Với 4 quan hệ chính lớn, nhỏ, bằng *và khác* sinh ra 6 toán hạng khi kết hợp với dấu bằng là <, <=, =, >=, >, !=. Kết quả của toán tử này là kiểu boolean với giá trị true/ false.

boolean isZero = 12 == 0;   // result is false
boolean greater3 =  12 > 3; // result is true
boolean notEqual = 12 != 0; // result is true

Toán tử so sánh là dạng toán tử hai ngôi và kết quả là 1 bit theo kiểu boolean. Đây là toán tử đơn giản nên ta không cần hình dung nó bằng hình ảnh. Ta đã sử dụng nó trong suốt thời gian dài trong quá trình học toán học ở phổ thông.

7. Ưu tiên Toán tử

Trong một biểu thức ta sẽ có nhiều toán tử cùng tồn tại. Chúng ta không thể tính nó cùng một lúc được. Do đó ta phải tính lần lược các toán tử đó theo một thức tự ưu tiên nào đó.

Thông thường các toán tử sẽ được ưu tiên từ trái sang phải. Điều đó không đủ tính linh hoạt để người sử dụng xây dựng công thức toán ngắn và đơn giản. Do đó các toán tử đã được chia làm nhiều nhóm và độ ưu tiên của các nhóm sẽ khác nhau. Ta có bảng ưu tiên các toán tử bên dưới thường được sử dụng để kiểm tra.

Loại Toán tử Thứ tự ưu tiên
Postfix  ()
[] . (dot)
Trái
sang phải
Unary  ++
– – ! ~
Phải
sang trái
Tính
nhân
*
/ % 
Trái
sang phải
Tính
cộng
+
– 
Trái
sang phải
Dịch
chuyển
>>
>>> <<  
Trái
sang phải
Quan
hệ
>
>= < <=  
Trái
sang phải
Cân
bằng
==
!= 
Trái
sang phải
Phép
AND bit
Trái
sang phải
Phép
XOR bit
Trái
sang phải
Phép
OR bit
Trái
sang phải
Phép
AND logic
&&  Trái
sang phải
Phép
OR logic
||  Trái
sang phải
Điều
kiện
?: Phải
sang trái
Gán =
+= -= *= /= %= >>= <<= &= ^= |= 
Phải
sang trái
Dấu
phảy
Trái sang phải

*Bảng 3 – Thứ tự ưu tiên các toán tử. Từ trên xuống và Từ trái sang phải. Nguồn tử Internet. *

Thứ tự ưu tiên của các toán tử do tiêu chuẩn toán học quy định nhưng người dùng có thể thay đổi nó theo ý mình muốn bằng cách sử dụng Postfix (Postfix được thể hiện trong Bảng  3 là (), [], . (dot).

Nhưng nếu trong biểu thức có nhiều postfix thì nó cũng được ưu tiên theo các cập postfixđộ sâu nhất sau đó đó rồi đến cha… cứ thế cho đến khi tính xong biểu thức đó.

![http://nguyenvantien2009.com/wp-content/uploads/2019/03/hinh_1.png](Hình 1 – Các bước thực hiện tính biểu thức dựa trên độ ưu tiên.)

Trong Hình 1 ta thấy phép toán + trên lý thuyết sẽ ưu tiên từ trái sang phải. Nhưng (…) đã thay đổi độ ưu tiên đó theo ý muốn người tạo ra biểu thức. Ta thực hiện các biểu thức nhỏ trong dấu ngoặc sau đó mới thực hiện biểu thức lớn. Quá trình thực hiện các biểu thức con trong đó có thể thực hiện song song nhau. Biểu thức lớn sẽ đợi cho 2 biểu thức con thực hiện xong mới thực hiện được.

Bạn có thể tham khảo mã nguồn ở https://github.com/tiennguyen2009/java-basic/tree/master/src/com/tiennguyen/chapter_2/operator

8. Tóm lược

Kiến thức về các toán tử trong số học là những kiến thức cơ bản ở phổ thông mà ta đã biết. Trong máy tính cũng sử dụng lại kiến thức đó để thực hiện trên số học. Nhưng toán tử số học mà ta thấy không phải là thứ mà máy tính làm toán thực sự mà hành động toán học được máy tính thực hiện là các toán tử dựa trên bit. Điều này dễ dàng thấy khi ta hiểu được các kiểu dữ liệu nguyên thủy của máy tính. Chúng được tạo nên từ nền tảng của bit.

Một đều quan trong nữa là ta cần nhớ cơ bản độ ưu tiên các toán tử trên máy tính (xem Bảng  3) và sử dụng (…), […] để thay đổi độ ưu tiến đó theo ý người dùng. Ta cũng không cần thiết phải nhớ hết độ ưu tiên trong bảng vì nó quá nhiều. Ta có thể “đối phó” bằng cách sử dụng (…) khi không rõ là phép toán nào ưu tiên hơn phép toán nào.

Toán tử trên số học, Toán tử gán và Toán tử quan hệ là các toán tử sử dụng rất nhiều trong lập trình. Do đó chúng ta cần tìm hiểu sâu về nó để có một nền tảng vững chắc để bắt đầu đi sâu vào nghiên cứu thuật toán trong ngành công nghệ thông tin.

  1. Toán tử bit: ~, &, |, ^
  2. Toán tử logic: &&, ||, !
  3. Toán tử số học: +, -, *, /, %
  4. Toán tử gán: =
  5. Toán tử quan hệ: <, <=, =, >=, >, !=

Trên là bảng tổng kết các toán tử chính trong 5 nhóm toán tử đã đề cập ở trên. Các toán tử khác sẽ được sinh ra bằng sự kết hợp các toán tử đó với nhau. Đơn giản đúng không?

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *