Chuyển đến nội dung chính

So sánh trong Java

1- Kiểu nguyên thủy và kiểu tham chiếu trong Java.

Trước hết chúng ta cần phân biệt kiểu nguyên thủy (Primitive type) và kiểu tham chiếu (reference type) trong java.
Trong Java chúng ta có 8 kiểu nguyên thủy.
  • Kiểu nguyên thủy (Primitive type)
TypeBit/BytesRange
boolean1 bitTrue or False
char16 bit/ 2 bytes0 to 65535
byte8 bit/ 1 byte-128 to 127
short16 bit/ 2 bytes-32768 to 32767
int32 bits/ 4 bytes-2147483648 to 2147483647
long64 bits/ 8 bytes-9,223,372,036,854,775,808 to -9,223,372,036,854,775,808
(-2^63 to -2^63)
float32 bits/ 4 bytes-3.4028235 x 10^38 to 3.4028235 x 10^38
double64 bits/ 8 bytes-1.7976931348623157 x 10^308 to 1.7976931348623157 x 10^308
Tất cả các kiểu khác đều mở rộng từ Object, chúng là các kiểu tham chiếu.

2- Stack và Heap là gì?

 
Biến của bạn được lưu trữ trên Stack (Ngăn xếp) hoặc Heap, vậy Stack và Heap là gì?
  • TODO

3- Kiểu nguyên thủy lưu trữ thế nào trên bộ nhớ

Trước hết bạn phải hiểu rằng, Java không đảm bảo rằng mỗi biến sẽ tương ứng với một vị trí trên bộ nhớ. chẳng hạn Java sẽ tối ưu theo cách biến 'i' sẽ được lưu trữ trên bộ đăng ký (register), hoặc thậm trí không được lưu trữ ở đâu cả, nếu trình biên dịch nhận thấy rằng bạn không bao giờ sử dụng giá trị của nó, hoặc nó có thể được dõi theo thông qua code và sử dụng các giá trị phù hợp một cách trực tiếp.
Xem một đoạn code:
?
1
2
3
4
5
6
7
8
9
// Create a variable 'a', and assin valud to it.
int a = 100;
 
// Assign new value
a = 200;
 
// Create variable b, assign value a
int b = a;
Khi đó Java thực hiện các thao tác sau:
  • TODO

4- Kiểu tham chiếu lưu trữ thế nào trên bộ nhớ

Khi bạn sử dụng toán tử new (Ví dụ new Object()), Java sẽ tạo ra một thực thể mới trên bộ nhớ. Bạn khai báo một biến và khởi tạo giá trị của nó thông qua toán tử new, chẳng hạn Object a = new Object(); Java sẽ tạo ra một thực thể mới trong bộ nhớ, và một tham chiếu 'a' trỏ tới vị trí bộ nhớ của thực thể vừa được tạo ra.

Khi bạn khai báo một biến b   Object b = a;  không có thực thể nào được tạo ra trong bộ nhớ, Java chỉ tạo ra một tham chiếu 'b', trỏ tới vị cùng vị trí mà 'a' đang trỏ tới.
?
1
2
3
4
5
6
7
8
// Khai báo và khởi tạo đối tượng.
Object a =  new Object();
 
// Khởi tạo lại đối tượng
a = new String("Text");
 
// Khai báo đối tượng 'b' và gán nó bằng đối tượng 'a'.
Object b =  a;
  • TODO

5- Các kiểu so sánh trong Java

Trong Java có 2 kiểu so sánh:
  • Sử dụng toán tử ==
  • Sử dụng phương thức (method) equals(..)
Toán tử == dùng so sánh các kiểu nguyên thủy và các kiểu tham chiếu.
 Toán tử equals(..) là method chỉ dùng cho các kiểu tham chiếu.

6- So sánh các kiểu nguyên thủy

Với kiểu nguyên thủy chúng ta chỉ có duy nhất một cách so sánh bằng toán tử ==, các kiểu nguyên thủy so sánh với nhau thông qua giá trị của chúng.
?
1
2
3
4
5
6
7
8
9
10
11
12
// Tạo ra một biến a, gán bởi giá trị 200
// Một vùng bộ nhớ (1) được tạo ra chứa giá trị 200.
int a = 200;
 
// Tạo ra một biến b, gán giá trị 200.
// Một vùng bộ nhớ (2) được tạo ra chứa giá trị 200.
int b = 200;
 
// Mặc dù 'a' và 'b' trỏ tới 2 vùng bộ nhớ khác nhau.
// So sánh a == b sẽ cho kết quả true.
// Vì kiểu nguyên thủy so sánh với nhau là giá trị.
boolean c = (a == b);

7- So sánh các kiểu tham chiếu

7.1- Sử dụng toán tử == so sánh các kiểu tham chiếu

Khi bạn so sánh 2 đối tượng tham chiếu theo toán tử ==, có nghĩa là so sánh vị trí mà 2 đối tượng tham chiếu này trỏ tới. Về bản chất là kiểm tra xem 2 tham chiếu đó có cùng trỏ tới một thực thể trên bộ nhớ hay không.
Hãy xem ví dụ:
ReferenceEeDemo.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package org.o7planning.tutorial.comparation;
 
public class ReferenceEeDemo {
 
public static void main(String[] args) {
 
    // Chú ý: Với String 2 cách khởi tạo đối tượng sau là không giống nhau:
    // Bạn có thể xem trong tài liệu nói về String.
    String str1 = "String 1";
    String str2 = new String("String 1");
 
    // Toán tử new tạo ra vùng bộ nhớ (1)
    // Chứa String "This is text"
    // Và s1 là một tham chiếu trỏ đến (1)
    String s1 = new String("This is text");
 
    // Toán tử new tạo ra vùng bộ nhớ (2)
    // Chứa String "This is text"
    // Và s2 là một tham chiếu trỏ đến (2)
    String s2 = new String("This is text");
 
    // Sử dụng toán tử == so sánh s1 và s2.
    // Kết quả ra false.
    // Nó rõ ràng khác với suy nghĩ của bạn.
    // Lý do là với kiểu tham chiếu
    // toán tử == so sánh vị trí mà chúng trỏ tới.
    boolean e1 = (s1 == s2); // false
 
    System.out.println("s1 == s2 ? " + e1);
 
    // Không có toán tử new nào.
    // Java tạo ra một tham chiếu có tên 'obj'
    // Và trỏ tới vùng bộ nhớ mà s1 trỏ tới.
    Object obj = s1;
 
    // 2 tham chiếu 'obj' và 's1' đang cùng trỏ tới 1 vùng bộ nhớ.
    // Kết quả trả về true
    boolean e2 = (obj == s1); // true
 
    System.out.println("obj == s1 ? " + e2);
}
}
Kết quả chạy chương trình

7.2- Sử dụng equals(..) so sánh các kiểu tham chiếu

StringComparationDemo.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package org.o7planning.tutorial.comparation;
 
public class StringComparationDemo {
 
  public static void main(String[] args) {
 
      String s1 = new String("This is text");
 
      String s2 = new String("This is text");
 
      // So sánh s1 và s2 thông qua method equals(..)
      boolean e1 = s1.equals(s2);
 
      // Kết quả sẽ là true
      System.out.println("first comparation: s1 equals s2 ? " + e1);
 
      
      s2 = new String("New s2 text");
 
      boolean e2 = s1.equals(s2);
 
      // Kết quả sẽ là false
      System.out.println("second comparation: s1 equals s2 ? " + e2);
  }
 
}
Kết quả chạy chương trình:

7.3- Ghi đè method equals(Object)

Phương thức equals(Object) là phương thức có sẵn của class Object, mọi class con đều được thừa kế method này. Trong một số tình huống bạn có thể ghi đè method này tại class con.
NumberOfMedals.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package org.o7planning.tutorial.comparation.equals;
 
// Số lượng Huy chương
public class NumberOfMedals {
 
  // Số huy chương vàng
  private int goldCount;
  // Số huy chương bạc.
  private int silverCount;
  // Số huy chương đồng
  private int bronzeCount;
 
  public NumberOfMedals(int goldCount, int silverCount, int bronzeCount) {
      this.goldCount = goldCount;
      this.silverCount = silverCount;
      this.bronzeCount = bronzeCount;
  }
 
  public int getGoldCount() {
      return goldCount;
  }
 
  public int getSilverCount() {
      return silverCount;
  }
 
  public int getBronzeCount() {
      return bronzeCount;
  }
 
  // Ghi đè method equals(Object) của class Object.
  @Override
  public boolean equals(Object other) {
      // Nếu đối tượng so sánh null trả về false.
      if (other == null) {
          return false;
      }
      // Nếu đối tượng cần so sánh không phải kiểu NumberOfMedals
      // thì trả về false.
      if (!(other instanceof NumberOfMedals)) {
          return false;
      }
      // Ép kiểu
      NumberOfMedals otherNoM = (NumberOfMedals) other;
      // Nếu số lượng huy chương vàng, bạc, đồng bằng nhau
      // trả về true.
      if (this.goldCount == otherNoM.goldCount
              && this.silverCount == otherNoM.silverCount
              && this.bronzeCount == otherNoM.bronzeCount) {
          return true;
      }
      return false;
  }
}
NumberOfMedalsComparationDemo.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.o7planning.tutorial.comparation.equals;
 
public class NumberOfMedalsComparationDemo {
 
   public static void main(String[] args) {
 
       // Thành tích của đội Mỹ
       NumberOfMedals american = new NumberOfMedals(40, 15, 15);
 
       // Thành tích của đội Nhật
       NumberOfMedals japan = new NumberOfMedals(10, 5, 20);
 
       // Thành tích của đội Hàn Quốc
       NumberOfMedals korea = new NumberOfMedals(10, 5, 20);
 
       System.out.println("Medals of American equals Japan ? "
               + american.equals(japan));
 
       System.out.println("Medals of Korea equals Japan ? "
               + korea.equals(japan));
   }
 
}
Kết quả chạy ví dụ

Vấn đề:

– Với các kiểu dữ liệu có sẵn, mình không bàn tới, nếu nó là int, char… thì có thể dùng dấu == để so sánh. Còn với các đối tượng khác, như String hay Date thì gọi hàm equals(), hai cách đem lại cùng một kết quả.
– Nhưng với các đối tượng bạn tự tạo ra thì sao? Xét đoạn chương trình sau:
//vd1
import java.util.Comparator;
import java.util.HashSet;
public class HelloWorld {
public static void main(String[] agrs) {
Person a = new Person();
Person b = new Person();
a.name=new String(“hiepnq”);
b.name=”hiepnq”;
a.age=22;
b.age=22;
HashSet set = new HashSet();
set.add(a);
set.add(b);
System.out.println(set.size());
}
}
class Person {
public String name;
public int age;
}
-> mặc dù 2 instance a và b có các cặp thuộc tính tương ứng bằng nhau (a.name.equals(b.name) ==true và a.age==b.age), nhưng khi in ra màn hình, HashSet set lại coi a và b là 2 đối tượng khác nhau, nên set.size() in ra lại là 2.
– Vấn đề này thoạt nhìn thì tưởng như chẳng có gì, nhưng nếu sử dụng trong kĩ thuật lập trình hiện đại, thì lại là một điều vô cùng tai hại: bạn sẽ khó có thể kiểm soát được việc so sánh 2 đối tượng, điều này khiến cho một số chức năng khác có liên quan tới so sánh sẽ không thực hiện được (vd: sắp xếp – tìm kiếm).
Vấn đề: Tại sao lại có hiện tượng như trên? Cơ chế nào khiến cho 2 đối tượng được gọi là bằng nhau?
– Trả lời cho vấn đề này, chúng ta tìm hiểu kĩ một chút về java nói riêng và các ngôn ngữ lập trình hiện đại nói chung (C# cũng tuân theo nguyên tắc này…). Trong bộ nhớ, khác với C/C++, Java quản lí đối tượng theo mã băm (hashCode), có nghĩa là: địa chỉ bộ nhớ các đối tượng sẽ được “băm” (hash) theo một công thức nào đó, trở thành một số int duy nhất, không trùng lặp khi trên cùng 1 máy tính.
– Tất cả các đối tượng trong Java đều có 1 gốc đối tượng cha duy nhất là Object, bản thân đối tượng Object có 2 phương thức: equals() và hashCode(). equals() sẽ dùng để so sánh các đối tượng, các lớp dẫn xuất từ Object có thể định nghĩa thế nào là bằng nhau nhờ Override hàm này (vd: String, Date …) và nếu chúng ta sử dụng thông thường, chúng ta cũng vẫn có thể sử dụng hàm này để so sánh, tham khảo đoạn code sau:
//vd2
import java.util.Comparator;
import java.util.HashSet;
public class HelloWorld {
public static void main(String[] agrs) {
Person a = new Person();
Person b = new Person();
a.name=new String(“hiepnq”);
b.name=”hiepnq”;
a.age=22;
b.age=22;
System.out.println(a.equals(b));
}
}
class Person {
public String name;
public int age;
@Override
public boolean equals(Object obj){
if(obj instanceof Person){
if(((Person)obj).name.equals(this.name)){
return true;
}
}
return false;
}
}
-> kết quả trả về là true, tốt rồi…
– Vấn đề kế tiếp được đặt ra: trong vd1 thì sao? nếu ta override hàm equals()? Lớp Person sẽ có dạng:
class Person {
public String name;
public int age;
@Override
public boolean equals(Object obj){
if(obj instanceof Person){
if(((Person)obj).name.equals(this.name)){
return true;
}
}
return false;
}
}
-> nhưng nếu thay vào vd1, thì set.size() vẫn là 2 – có nghĩa là: khi ta sử dụng, thì ta có thể coi là bằng nhau khi Perso.name bằng nhau, nhưng với các collection Set và Map (cả HashTable) thì chúng không cho rằng 2 đối tượng trên đã bằng nhau? tại sao vậy?
– Như đã nói ở trên, Object còn có hàm hashCode() trả về mã băm của đối tượng, mặc định, hai đối tượng được coi là bằng nhau, nếu chúng có hashCode() bằng nhau, có nghĩa là, chúng trỏ tới cùng 1 đối tượng trên vùng nhớ HEAP… Vậy, để khắc phục điều này, ta cần phải override cả hàm hashCode() nữa…
import java.util.Comparator;
import java.util.HashSet;
public class HelloWorld {
public static void main(String[] agrs) {
Person a = new Person();
Person b = new Person();
a.name=new String(“hiepnq”);
b.name=”hiepnq”;
a.age=22;
b.age=22;
HashSet set = new HashSet();
set.add(a);
set.add(b);
System.out.println(set.size());
}
}
class Person {
public String name;
public int age;
@Override
public boolean equals(Object obj){
if(obj instanceof Person){
if(((Person)obj).name.equals(this.name)){
return true;
}
}
return false;
}
@Override
public int hashCode(){
return age;
}
}
-> giờ thì kết quả là 1 rồi
– Vậy, muốn đối tượng bằng nhau trọn vẹn khi và chỉ khi: hàm equals() trả về true, và hàm hashCode() trả về cùng một giá trị.

Kết luận:

– Định nghĩa 2 đối tượng bằng nhau được coi là 1 trong những vấn đề cốt lõi và then chốt nhất trong Core Java và cả C# căn bản nữa, nắm bắt được yếu tố này, các bạn có thể tự định nghĩa các đối tượng bằng nhau một các rất mềm dẻo

Nhận xét

Bài đăng phổ biến từ blog này

Entry Test của FPT

IQ - Kiểm tra tư duy logic (8/20) - GMAT- Kiểm tra khả năng tính toán trong thời gian ngắn (8/20) - Tiếng Anh (18-> 25/50) - Các bài thi chuyên môn - FE (8/20) IQ: lên mạng tìm "IQ test" là ra đầy. + GMAT: Những câu trắc nghiệm tính toán đơn giản kiểu như sau:  1 . Một shop thời trang sale off quần jeans 15 %, quần jeans giá 450 $, người mua đưa 500 $, hỏi cashier trả lại bao nhiêu $ tiền thừa.? 2 . 100 % là 180 , vậy 150 là bao nhiêu %? Tiếng anh: Cỡ như thi TOEIC thôi. Chuyên môn: Mobile thì trắc nghiệm Java. Qúa trình tuyển như sau :v Lần 1: Test IQ, Tiếng Anh( mình làm í ẹ khoảng 50% mà vẫn được) , Java Lân 2: được gọi điện lên :)) + Gioi thiệu bản thân + Họ chỉ hỏi các câu căn bản như: -. OOP: là gì, 4 tính chất, ví dụ, khác nhau giữa interface và abstract - CODE: hầu toàn các bài toán vòng for :)) , cẩn thận mấy câu kế thừa. SQL (distinct, view, function, cursor, store procedure, ...v.v.), nhớ có câu cộng 2 số int không dùng biến đệm hơi khoai haha + Nói ch...

Java: Java Package-Thư viện trong Java

Giới thiệu về Package Các bạn mới học lập trình Java thường không dể ý tới package vì các bạn toàn tạo file .java vào cùng 1 chỗ, không cần sắp xếp, không cần quản lý truy nhập. Nhưng để tăng kỹ năng lập trình với Java, các bạn cần phải tìm hiểu về package trong Java. Các bạn có thể tham khảo định nghĩa sau: Package được dùng để đóng gói các lớp trong chương trình lại với nhau thành một khối. Đây là cách tốt nhất để lưu trữ các lớp gần giống nhau hoặc có cùng một module thành một khối thống nhất – để đáp ứng 1 khối chức năng. Từ đây mình sẽ giới thiệu thêm với các bạn các câu lệnh nhâp khẩu,nó có định dạng như sau : Định dạng :  import javaPackageNameImport;    Nó giống như khai báo thư viện ở các ngôn ngữ lập trình khác.Như vậy,chỉ khi các bạn nhập khẩu chúng,các bạn mới có thể sử dụng thư viện mà chúng cung cấp cho ta. VD :    import java.util.Date;   import java.text.SimpleDateFormat; Lưu ý : -Các câu lệnh nhập khẩu rất nhiều và...

phỏng vấn Embedded C và C++?

Có vài điểm rất nhỏ, các bạn không để ý có thể dẫn đến bất lợi (nếu không muốn nói là rớt) ở vòng hồ sơ hoặc khi đi phỏng vấn. Mình liệt kê rất cụ thể ra những chuyện mình đã thấy qua nhiều lần (người thật, việc thật), hy vọng cho các bạn thêm kinh nghiệm. Lưu ý: Những chuyện này rất chủ quan, có thể chỉ đúng trong môi trường của mình và hoàn toàn không đúng trong môi trường khác. Gửi email CV đến không có Cover Letter, chỉ đính kèm mỗi file PDF là cái CV. CV ghi : tiếng Anh: Trung Bình, C: Trung Bình,... nhưng bên dưới lại ghi ưu điểm_:  là người ham học hỏi... CV bằng tiếng Việt và lại viết sai chính tả tiếng Việt. CV ghi quá nhiều về các hoạt động tình nguyện, hiến máu, blah blah... nhưng phần kĩ thuật lại có 3 dòng thôi. Mặc đồ thể dục của trường và đi dép lê đến PV. Đến muộn PV (em ấy bảo bị kẹt xe). Đưa yêu cầu, em ấy không hiểu đề nhưng ngại không dám hỏi lại, dẫn đến viết hoàn toàn sai. Hỏi ngay câu căn bản đầu tiên em ấy đã nói không biết và  đổ do trường kh...