Các bước làm việc với tập hợp các đối tượng
Ở bài trước tôi đã đề cập đến cách tạo 1 đối tượng trong java. Tuy nhiên trong quá trình làm việc, ít khi ta phải làm việc độc lập với 1 đối tượng mà thông thường ta phải làm việc với 1 tập hợp các đối tượng. Ví dụ, ta đã đặc tả 1 sinh viên nhưng khi thực sự làm việc thì ta lại làm việc với 1 tập các sinh viên (lớp học chẳng hạn). Chính vì lý do đó, trong bài này tôi sẽ trình bày cách chugn1 ta tạo tập hợp các đối tượng.
Trước hết, ta nhắc lại vài điểm quan trọng trong phần đặc tả đối tượng.
Thứ nhất ta phải xác định thuộc tính khóa để định nghĩa 2 phương thức equals cùng hashCode cho thật chính xác. Tiếp theo ta phải đảm bảo cách đặc tả các thuộc tính với cách đặt tên theo chuẩn. Đây là code của bài trước, tập tin Thisinh.java
Bây giờ ta xây dựng phần tập hợp các đối tượng. Giả sử ta đặt tên cho tập hợp này là phòng thi (file Phongthi.java). Mô hình làm việc ở đây tương tự như mô hình quan hệ thực thể 1 – nhiều trong phần cơ sở dữ liệu – Một phòng thi chứa nhiều thí sinh.
Phòng thi cũng được xem như là 1 đối tượng, nghĩa là nó cũng có những tính chất và các hành vi của nó, ngoài ra nó còn chứa 1 danh sách các thí sinh dự thi. Các thuộc tính cơ bản của 1 phòng thi như: mã số phòng thi (msPT), địa chỉ phòng thi (diachiPT), lượng thí sinh tối đa(luongTS). Ngoài ra còn có rất nhiều thuộc tính khác nhưng để cho đơn giản ta giả sử chỉ có vài thuộc tính vừa nêu.
| package descript;
import java.util.ArrayList; public class Phongthi { //… |
Dĩ nhiên chúng ta phải định nghĩa các phương thức setters/getters cho các thuộc tính msPT, diachiPT, luongTS, các constructor và cũng phải định nghĩa thuộc tính phân biệt giữa các phòng thi. Code cho các phương thức này như sau:
| public String getMsPT() { return msPT; } public void setMsPT(String msPT) { this.msPT = msPT; } public String getDiachiPT() { return diachiPT; } public void setDiachiPT(String diachiPT) { this.diachiPT = diachiPT; } public int getLuongTS() { return luongTS; } public void setLuongTS(int luongTS) { this.luongTS = luongTS; } //constructors public Phongthi() { this(“”,”",0); } public Phongthi(String msPT, String diachiPT, int luongTS) { super(); this.msPT = msPT; this.diachiPT = diachiPT; this.luongTS = luongTS; dsTS=new ArrayList<Thisinh>(); } //=================== @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((msPT == null) ? 0 : msPT.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Phongthi other = (Phongthi) obj; if (msPT == null) { if (other.msPT != null) return false; } else if (!msPT.equals(other.msPT)) return false; return true; } @Override String toString(){ |
Tiếp theo chúng ta định nghĩa các phương thức cơ bản cho tập hợp gồm: thêm, xóa, sửa, lấy thông tin.
- Với phương thức thêm 1 thí sinh mới vào phòng thi, ta định nghĩa như sau:
| /** * Thêm 1 thí sinh vào phòng thi có kiểm tra trùng mã * @param ts: thí sinh thêm vào * @return true nếu việc thêm thành công */ public boolean ThemThisinh(Thisinh ts) { //Nếu thí sinh đã tồn tại thì không cho thêm if(dsTS.contains(ts)) return false; if(dsTS.size()+1>luongTS)//nếu đã đủ lượng thí sinh return false; return dsTS.add(ts); } |
Làm sao chương trình chúng ta phát hiện 2 thí sinh giống nhau chỉ dự vào biểu thức kiểm tra dsTs.contains(ts)?
Điều này là hoàn toàn có thể bởi java sẽ kiểm tra chúng dựa trên phương thức equals của đối tượng. Với đối tượng của chúng ta thì 2 thí sinh sẽ là 1 nếu chúng có cùng số báo danh. Lợi hại chưa?
- Phương thức tiếp theo sẽ là xóa 1 thí sinh sẽ phòng thi. Phương thức này như sau:
| /** * Xóa 1 thí sinh khỏi phòng thi * @param soBD là số báo danh của thí sinh cần xóa * @return trả về true nếu xóa thành công */ public boolean XoaThisinh(String soBD)throws Exception { Thisinh ts=new Thisinh(soBD,”",0f,0f,0f); if(!dsTS.contains(ts)) return false; return dsTS.remove(ts); } |
- Phương thức cập nhật thông tin thí sinh trong phòng thi
| /** * Sửa thông tin thí sinh * @param soBD: số DB của thí sinh cần sử thông tin * @param newTS: thông tin mới cần cập nhật * @return true nếu sửa chữa thành công */ public boolean SuaThongtinTS(String soBD, Thisinh newTS)throws Exception { Thisinh ts=new Thisinh(soBD,”",0f,0f,0f); if(!dsTS.contains(ts)) return false; //lấy ts cần thay đổi thông tin ts=dsTS.get(dsTS.indexOf(ts)); //cập nhật ts.setHoTen(newTS.getHoTen()); //ts.setDiemHoa(newTS.getDiemHoa()); //ts.setDiemLy(newTS.getDiemLy()); //ts.setDiemToan(newTS.getDiemToan()); return true; } |
- Phương thức lấy 1 thí sinh ra khỏi danh sách khi biết số báo danh
| /** * Lấy thông tin của 1 thí sinh khi biết số báo danh * @param soBD số báo danh của ts cần lấy thông tin * @return null nếu không lấy được */ public Thisinh LayThongtinTS(String soBD) throws Exception{ Thisinh ts=new Thisinh(soBD,”",0f,0f,0f); if(!dsTS.contains(ts)) return null; ts=dsTS.get(dsTS.indexOf(ts)); return ts; } |
- Phương thức lấy 1 thí sinh ra khỏi danh sách khi biết số thứ tự của thí sinh trong danh sách:
| /** * Lấy thông tin của 1 thí sinh khi biết * số thứ tự của ts đó trong danh sách * @param index :số thứ tự của ts * @return null nếu không thành công * @throws Exception */ public Thisinh LayThongtinTS(int index)throws Exception{ if(index<0||index>dsTS.size()) return null; return dsTS.get(index); } |
- Phương thức lấy số thí sinh thực sự đang có trong phòng
/** |
Và đây là code hoàn chỉnh của lớp này.
Bây giờ ta có thể viết code cho phần thử nghiệm của lớp này rồi đó.
Driver Class
Ở đây ta viết một ứng dụng đơn giản ở chế độ console hiển thị 1 menu cho người dùng chọn lựa chức năng. Và đây là code hoàn chỉnh:
package descript;
import java.util.Scanner; public class QuanLyPhongthi { static void ThemTS() { static void layTTTS_chiso() { static void InDanhsachTS() { |
Kết quả như sau:

Chúc thành công!
VoDanh said
Xin chào, tôi là người mới học Java, tình cờ gặp trang này khá hay, nhưng tôi có thắc mắc về 1 số, mong bạn giải thích:
Đoạn code này:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((msPT == null) ? 0 : msPT.hashCode());
return result;
}
Tôi thắc mắc tại sao method hashCode lại dùng prime*result+….
sao ko thay prime=31 và result bằng 1 luôn. Như thế có vấn đề gì ko nhỉ? Và tại sao lại đặt prime là 31 mà ko phải là 1 số khác?
@Override nghĩa là gì? Có cần thiết trong bài ko??
Mong bạn sớm giải thích, cảm ơn
Võ Văn Hải said
Phương thức hashCode dùng như là 1 cách đánh chỉ số (indexing) trong 1 tập hợp các đối tượng để cho việc truy xuất nó thực hiện nhanh hơn. Trong 1 tập hợp các đối tượng thì không có 2 đối tượng cùng index.
Prime là 1 con số nguyên tố, nó rất hữu ích trong việc mã hóa. Ở đây cố ý khai báo prime riêng ra bởi chúng ta có thể thay nó bằng 1 con số nguyên tố bất kỳ nào. Bạn có thể viết lại hasCode cho gọn hơn như sau:
public int hashCode() {
return msPT.hashCode();
}
Ở đây @Override là 1 anotaion để chỉ việc đây là phương thức được override lại từ lớp Object. Tuy nhiên, bạn cũng có thể không thêm anotation này cũng không sao cả nhưng có nó mọi thứ rõ ràng hơn.
hochanh said
-cho tôi hỏi :chúng ta phải định nghĩa các phương thức setters/getters cho các thuộc tính msPT, diachiPT, luongTS,..
“public String getMsPT() {
return msPT;”
tại sao mình phải return msPT? phải chăng lúc nào mình định nghĩa pthuc get là bắt buộc return hay sao vậy.?
-và cái dòng này có ý nghĩa ra sao vậy?
//constructors
public Phongthi() {
this(””,””,0);
}
xin cảm ơn.
VoDanh said
Xin chào, rất vui được gặp lại. Cảm ơn bạn đã giải đáp thắc mắc của tôi. Vấn đề của bạn hochanh tôi xin mạn phép giải thích như sau, nếu sai mong bạn sửa dùm:
Phương thức getMsPT có mục đích là lấy MsPT như tên gọi của nó, dĩ nhiên khi định nghĩa method đã xác định kiểu trả về là String, lẽ nào lại không return?? phương thức get với mục đích trả về 1 thông tin nào đó thì tất nhiên phải return, trừ khi bạn không muốn nó in ra ngay thông tin mà ko cần trả về, khi đó hãy sử dụng void thay vì String. Đây là vấn đề cơ bản nếu như bạn đã từng học qua lập trình.
Constructors Phongthi ở trên là 1 cách để gọi 1 constructor khác của lớp và truyền các tham số mặc đinh cho các thuộc tính của đối tượng, cụ thể là đây:
public Phongthi(String msPT, String diachiPT, int luongTS) {
super();
this.msPT = msPT;
this.diachiPT = diachiPT;
this.luongTS = luongTS;
dsTS=new ArrayList();
}
cách làm này rất hay, ngắn gọn tuy nhiên tôi có 1 thắc mắc là trong lúc Runtime thì cách viết cụ thể (gán các thuộc tính trực tiêp=”" hoặc 0) so với cách trên thì cái nào sẽ thực hiện nhanh hơn, hoặc là như nhau nhỉ??
VoDanh said
OK, bây giờ xin đi vào vấn đề chính tôi muốn bàn luận (thắc mắc) với bạn hôm nay. Tôi đã thử test ví dụ trên, mặc dù khá súc tích, khá dễ hiểu. Tuy nhiên tôi xin có 1 chút thắc mắc sau:
-Phần lấy, sửa và xóa thông tin thí sinh dựa vào Số Báo Danh có vẻ chưa thực hiện tốt chức năng của nó.
Cụ thể, khi tôi gọi method lấy thông tin thí sinh và truyền cho nó 1 số BD đã tồn tại trong danh sách, theo như cách thực hiện của method thì nó sẽ tạo 1 đối tượng với các tham số mặc định ‘rỗng’, chỉ có duy nhất phần soBD là đùng.
new Thisinh(soBD,”",0f,0f,0f);
Như thế nếu dùng method contains của ArrayList để xem trong danh sách có phần tử nào tương tự không thì có lẽ chưa đúng. lẽ nào ArrayList chỉ dựa vào soBD của phần tử Thisinh để xác định??
Tiện đây xin hỏi phương thức contains của ArrayList có tương tự với phương thức equals mà chúng ta hay Override ko nhỉ?
bạn có thể nói rõ hơn 1 chút về cách khởi tạo 1 đối tượng 1 đối tượng kiểu ArrayList lưu danh sách của 1 đối tượng khác không?
Cách viết này:
dsTS=new ArrayList();
Tại sao không cho phép gọi ds TS=new ArrayList(Thisinh) nhỉ? CÒn cách gọi nào khác tương tự để sử dụng ArrayList không?
Có lẽ tôi thắc mắc hơi nhiều, tuy nhiên mong bạn trả lời nếu có thời gian rảnh
Thanks!
hochanh said
cho hỏi nếu tôi không muốn khai báo phương thức kiểu:
private String msPT;
private String diachiPT;
private int luongTS;
mà thay vào đó là kiểu:
protected String msPT;
protected String diachiPT;
…………….int LuongTS;
hoặc kiểu:
public String msPT;
public String diachiPT;
public int LuongTS;
===> thì nó có khác nhau về bản chất gì không bạn.Và bạn hảy giãi thích dùm mình rõ cách thức làm việc của 3 phương thức đó dùm.Thân
VoDanh said
Xin chào, vấn đề thắc mắc bên trên của tôi đã được giải quyết, cảm ơn bạn. Có lẽ phương thức contains nó sẽ sử dụng phương thức equals của loại đối tượng mà nó chứa để so sánh bằng cách lặp từng phần tử trong danh sách. Lẽ ra tôi nên hỏi là phương thức contains sẽ sử dụng phương thức equals để làm việc thi rõ ràng hơn, luc đầu tôi cứ nghĩ nó sẽ so sánh toàn bộ các thuộc tính của đối tượng mà nó lưu trữ.
Vấn đề của bạn hochanh xin mạn phép giải thích như sau:
Bạn nên tìm hiểu lại phạm vi truy cập của các biến (hoặc đối tượng, phương thức) khi sử dụng từ khóa private, protect, public hay default cho nó. Nếu bạn muốn 1 lớp con có thể sử dụng được các biến đã khai bào khi extends từ 1 lớp bạn đã định nghĩa thì bạn nên sử dụng từ khóa protect.
Sở dĩ thường dùng private để khai báo các biến vì tính chất bao đóng của nó(encapsulation). nếu không dùng private, các lớp khác có thể truy cập trực tiếp và thay đổi giá trị của các biến này. Điều này có thể gây rắc rối cho chương trình nếu như giá trị đó không phù hợp. Trong trường hợp này ta sẽ phải “gây khó khăn” để chỉ cho phép thay đổi giá trị của biến trong phạm vi mà ta quy định.
Ví dụ như để nhập tuổi của 1 thí sinh bạn sẽ dùng phương thức setAge(int tuoi) để hạn chế độ tuổi mà người dùng, hoặc có thể là chính bạn sẽ nhập vào.
Có lẽ nên để thầy giải thích tiếp..
java said
ban oi sao minh copy ve lam ko chay vay? bun nhi?
java said
co bai tap nao ma chay lun hok ban..gian giong the cung dc
lov3code said
Chào bạn ! bạn viết này rất hay nhưng mình có thắc mắc là bạn dùng phương thức set có kiểm tra rằng Họ tên và số báo danh không được để trống trong khi Trong các phương thức XoaTS, SuaTTTS, LayTTTS thì bạn lại dùng hàm khởi tạo là
ThiSinh ts = new ThiSinh(strSoBD,“” ,0f,0f,0f);
Như vậy khi tạo đối tượng Temp này sẽ bị báo lỗi : “Họ Tên Không Thể Không Có ” .do ThiSinh có họ Tên Trống = “” ( mình đã chạy test thử bằng NetBean 6.9 )
Giải pháp của mình là viết các constructor cho các đối tượng thi Sinh temp
có tên không để trống
đơn giản như ThiSinh tsTemp = new ThiSinh(strSoBD,”no name”,0f,0f,0f);
Duy said
Bài hay quá, mình có 1 câu hỏi nhỏ, mình mới biết java thôi. Chỗ này:
public Phongthi(String msPT, String diachiPT, int luongTS) {
super();
this.msPT = msPT;
this.diachiPT = diachiPT;
this.luongTS = luongTS;
dsTS=new ArrayList();
}
Mình dùng phương thức như vầy
public Phongthi(String msPT, String diachiPT, int luongTS) throws Exception {
try{
super();
setmsPT(mspt);
setdiachiPT(diachiPT);
setluongTS(luongTS);
dsTS=new ArrayList();}
}catch (Exception e)
{
System.out.println (ex.getMessage());
}
mình bắt lỗi ngay đây có được không? Và cách viết trên có đúng không? mình có đem super() ra khỏi vòng try không vì mình thấy có vẻ như nhau. Cám ơn bạn
Võ Văn Hải said
Bạn không nên tạo constructure như vậy. Đúng ra constructor chính xác nhất sẽ là:
public Phongthi(String msPT, String diachiPT, int luongTS) throws Exception { setmsPT(mspt); setdiachiPT(diachiPT); setluongTS(luongTS); dsTS=new ArrayList();} }Việc bắt lỗi sẽ dành cho phía người dùng lớp này. như vậy để dùng được họ phải try/catch hoặc lại tiếp tục throws Exception. Như vậy gọi là “checked exception”.
Trần Văn Hải said
Mình thấy phần tập hợp trong java sao giống mảng các đối tượng quá. Vậy có thể cho mình hỏi sự khác nhau giữa mảng các đối tượng và tập hợp không. Khi nào nên dùng mảng và khi nào nên dùng tập hợp. Cảm ơn nhiều, rất mong nhận được hồi âm sớm!
Duy said
cho mình hỏi, trong 1 tập các đối tượng A(msPT, tenPT, LuongTS, maSV) B(maSV, tenSV, diem) muốn cho B.maSV=với A.maSV để xuất thông tin của cả A và B được không? Mình cố thử làm tuy nhiên do @Override đã ghi nhận msPT là key nên ko thê lấy msVS ra gán đc.
Jimmy said
Thưa thầy,em có
class point { public String name
public double x ;
public double y ;
public double z ;
…..
public double distanceTo(point B){
point A=this;
return ((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z));}}
Em có tập X (có m point),tập Y ( có n point).Em cần phải tính distance của các point trong X,distance của các point trong Y và in ra tên những cặp point trong X có distance bằng với những cặp point trong Y.
Mong thầy và các bạn gợi ý cho em cách làm,sử dụng Hashtable hoặc các CTDL khác.
Chuc thay vui,khoe.
Võ Văn Hải said
OOP giải quyết dạng này bằng cáh trong A có 1 collection của B hoặc trong B có 1 collection của A chứ không phải theo kiểu như khóa ngoại của cơ sở dữ liệu quan hệ.
Võ Văn Hải said
Mảng truy xuất 1 cách tuần tự còn tập hợp (có thể) truy xuất theo dạng ngẫu nhiên. Hơn nữa, nếu dùng mảng bạn phải code từ A-Z, còn dùng theo kiểu tập hợp xây dựng sẵn của java bạn chỉ cần gọi…..
Duy said
Cám ơn bạn mình đã hiểu. Rất hay
VietHung said
Thua thay,em co Hastable;
nhieu Key co chung mot value hoac nguoc lai; nhu vay co duoc khong thay?
Võ Văn Hải said
Nguyên lý của ánh xạ trong Map của java là đơn ánh nên không thể có trường hợp như bạn nói được.
VietHung said
Thầy help em chút,em rất ngạc nhiên vì kết quả:
import java.util.LinkedList;
public class superposition101 { public static void main(String[] args)throws Exception{
LinkedList list= new LinkedList();
String s1=”12.124″;
String s2=”0.125″;
String s3=”120.124″;
double x=Double.parseDouble(s1);
double y=Double.parseDouble(s2);
double z=Double.parseDouble(s3);
point P= new point(x,y,z);
list.add(P);
double x1=Double.parseDouble(s1);
double y1=Double.parseDouble(s2);
double z1=Double.parseDouble(s3);
point P1= new point(x1,y1,z1);
System.out.print(list.contains(P1));
}}
class point {
public double x ;
public double y ;
public double z ;
public point(double x,double y,double z) {
this.x = x;
this.y = y;
this.z = z;}
}
In ra màn hình : False(tại sao không phải là True vì P1 và P là một điểm).
Thanks Thay nhieu
Võ Văn Hải said
2 điểm khác nhau chứ bạn! -:).
Nó được lưu vào 2 vùng nhớ khác nhau và vì thế chúng khác nhau. Bạn muốn biểu diễn rằng nó là 1 khi cùng tọa độ thì trong lớp Point bạn phải override phương thức equals và hashcode. trong đó bạn khai báo 2 Poit bằng nhau khi nào.
VietHung said
Thầy ơi: sao phương thức equal ở lớp Phongthi của thầy phức tạp quá,theo em ,làm vậy là đủ xài rồi:
@Override
public boolean equals(Phongthi P){
if(this.msPT = P.msPT) return true;
else return false;
}
còn phương thức hashcode:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((msPT == null) ? 0 : msPT.hashCode());
return result;
}
(msPT == null) ? 0 : msPT.hashCode() có nghĩa là sao thầy,có cách nào bình dân hơn không thầy.
Cám ơn thầy
Võ Văn Hải said
1. với equals bạn phải kiểm tra đủ các trường hợp như null,… chứ.
2. (msPT == null) ? 0 : msPT.hashCode() có nghĩa nếu mspt là null thì kết quả là 0 còn không thì là mspt.hashcode. Đây là kiểu toán tử 3 ngôi thay vì if..else.