Bài giảng Lập trình hướng đối tượng - Phần 2 - Nguyễn Mạnh Sơn

pdf 152 trang hoanguyen 4240
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Lập trình hướng đối tượng - Phần 2 - Nguyễn Mạnh Sơn", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfbai_giang_lap_trinh_huong_doi_tuong_phan_2_nguyen_manh_son.pdf

Nội dung text: Bài giảng Lập trình hướng đối tượng - Phần 2 - Nguyễn Mạnh Sơn

  1. CHƯƠNG 6 LẬP TRÌNH GIAO DIỆN TRÊN JAVA Nội dung chương này tập trung trình bày các vấn đề liên quan đến lập trình giao diện, với sự hỗ trợ của một số đối tượng được cung cấp sẵn bởi Java: Lập trình giao diện với các đối tượng cơ bản và với các đối tượng multimedia. Lập trình giao diện với HTML&Applet Lập trình giao diện với SWING 6.1 GIAO DIỆN VỚI CÁC ĐỐI TƯỢNG CƠ BẢN Trong mục này, chúng ta sẽ tìm hiểu và sử dụng các đối tượng cơ bản của lập trình giao diện trong Java: Các đối tượng khung chứa (container) cơ bản: Frame, Panel, Dialog. Các đối tượng thành phần (component) cơ bản: Button, Label, TextField, TextArea Các sự kiện cơ bản của các đối tượng. Muốn sử dụng các đối tượng này, cần thêm lệnh sử dụng thư viện awt của Java: import java.awt.*; 6.1.1 Các đối tượng container cơ bản Các đối tượng container được dùng để chứa các đối tượng thành phần khác. Các lớp đối tượng này có một số phương thức chung như sau: add(Object): Thêm một đối tượng (kiểu component) vào container. remove(Object): LoạPTITi bỏ một đối tượng ra khỏi container. removeAll(): Loại bỏ tất cả các đối tượng mà container đang chứa. getComponent(int): Trả về đối tượng thành phần có chỉ số là tham số đầu vào. Container quản lí các đối tượng chứa trong nó dưới dạng mảng. Chỉ số của các thành phần là số thứ tự khi thành phần đó được thêm vào container. getComponents(): Trả về mảng tất cả các đối tượng mà container đang chứa. countComponents(): Trả về số lượng các đối tượng mà container đang chứa. Frame Frame là một đối tượng có thể dùng một cách độc lập, hoặc được gắn vào một đối tượng khác như một đối tượng conponent bình thường. Thông thường, Frame được dùng như một cửa sổ của một chương trình độc lập. Các phương thức cơ bản của lớp Frame: Frame(): Khởi tạo không tham số. 135
  2. Frame(String): Khởi tạo với tham số là dòng tiêu đề của frame. setSize(int, int): Định kích cỡ của frame, tham số tương ứng là chiều rộng và chiều cao của frame. setVisible(boolean): Cho phép frame xuất hiện hay ẩn đi trên màn hình. setTitle(String)/getTitle(): Truy nhập thuộc tính dòng tiêu đề của frame. setResizable(boolean): Thiết lập thuộc tính cho phép thay đổi kích cỡ frame. setIconImage(Image): Thiết lập ảnh icon ở góc trên (biểu tượng) của frame. Chương trình 6.1 minh hoạ việc sử dụng một đối tượng của lớp Frame. Chương trình 6.1 package vidu.chuong6; import java.awt.*; public class FrameDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“This is my Frame!”); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } PTIT Hình 6.1: Kết quả demo Frame Panel Panel cũng là một dang khung chứa, nhưng khá đơn giản. Panel chỉ dùng để nhóm các đối tượng giao diện với nhau. Thông thường, panel được dùng trong một cửa sổ của Frame hoặc một ứng dụng khác. Các phương thức cơ bản của lớp Panel, ngoài các phương thức chung của container: Panel(): Khởi tạo không tham số. Chương trình 6.2 minh hoạ việc sử dụng một Panel trong một Frame. Chương trình 6.2 136
  3. package vidu.chuong6; import java.awt.*; public class PanelDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has a panel!”); myFrame.setSize(300,150); // Định kích cỡ frame Panel myPanel = new Panel();// Khai báo panel myFrame.add(myPanel); // Thêm panel vào frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.2: Kết quả demo Panel Dialog Dialog là một đối tượng cửa sổ con của một cửa sổ chương trình chính. Do vậy, Dialog chỉ được sử dụng kèm với một Frame. Có hai dạng Dialog: Modal: Khi hiện của sổ dialog, các cửa sổ khác của chương trình sẽ bị khoá lại, không thao tác được, chỉ thao tác được trên cửa sổ dialog. Non-modal: Không khoá PTITcác cửa sổ khác. Khi dialog xuất hiện, người dùng vẫn có thể chuyển sang thao tác trên các cửa sổ khác, nếu cần. Các phương thức cơ bản của lớp Dialog: Dialog(Frame, boolean): Khởi tạo dialog, tham số thứ nhất là frame chứa dialog, tham số thứ hai xác định dialog có là modal hay không. Dialog(Frame, String, boolean): Khởi tạo dialog, thêm tham số thứ hai là dòng tiêu đề của dialog. setVisible(boolean): Thiết lập trạng thái hiển thị hoặc ẩn dialog trên màn hình. setSize(int, int): Định kích cỡ cho dialog, các tham số tương ứng là chiều rộng và chiều cao của dialog. setTitle(String)/getTitle(): Truy nhập thuộc tính dòng tiêu đề của dialog. setResizable(boolean): Thiết lập thuộc tính cho phép thay đổi kích cỡ của dialog. 137
  4. setLayout(Layout): Thiết lập chế độ hiển thị các đối tượng chứa trong dialog. Chương trình 6.3 minh hoạ việc thêm một dialog (đang rỗng, chưa có đối tượng thành phần nào) vào một frame. Chương trình 6.3 package vidu.chuong6; import java.awt.*; public class DialogDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has a dialog!”); myFrame.setSize(300,150); // Định kích cỡ frame // Khai báo và khởi tạo dialog Dialog myDialog = new Dialog(myFrame, “An empty dialog!”, true); myDialog.setSize(300,150); // Định kích cỡ dialog myDialog.setVisible(true); // Hiển thị dialog } } PTIT Hình 6.3: Kết quả demo Dialog 6.1.2 Các đối tượng component cơ bản Các đối tượng component được dùng để làm thành phần của các đối tượng khung chứa, chúng không thể dùng độc lập, mà luôn phải gắn vào trong một đối tượng khung chứa container. Label Label (nhãn) là một đối tượng để hiển thị văn bản tĩnh, những văn bản mà người dùng không thể thay đổi trực tiếp được. Các phương thức cơ bản của Label: Label(): Khởi tạo một nhãn rỗng. Label(String): Khởi tạo một nhãn với nội dung văn bản là tham số đầu vào. 138
  5. Label(String, int): Khởi tạo một nhãn có nội dung sẵn, tham số thứ hai xác định cách căn lề của nhãn so với khung chứa, bao gồm {Label.CENTER, Label.LEFT, Label.RIGHT}. setText(String)/getText(): Truy nhập nội dung văn bản của nhãn. setAlignment(int)/getAlignment(): Truy nhập thuộc tính căn lề của nhãn. setFont(Font): Định dạng phông chữ của nhãn. Chương trình 6.4 minh hoạ việc sử dụng nhãn trong một frame. Chương trình 6.4 package vidu.chuong6; import java.awt.*; public class LabelDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has a label!”); myFrame.setSize(300,150); // Định kích cỡ frame // Khai báo và khởi tạo label Label myLabel = new Label(); myLabel.setText(“This is a label!”);//Gán nội dung văn bản myLabel.setAlignment(Label.CENTER);// Căn lề giữa myFrame.add(myLabel); // Gắn label vào frame myFrame.setVisible(true);PTIT // Hiển thị frame } } Hình 6.4: Kết quả demo Label TextField và TextArea Đây là hai đối tượng dùng để biểu diễn văn bản và người dùng có thể thay đổi nội dung văn bản chứa trong chúng. Điểm khác biệt là TextField chỉ cho phép một dòng văn bản, trong khi TextArea cho phép chứa nhiều dòng văn bản. Các phương thức chung của hai lớp này: 139
  6. setText(String)/getText(): Truy nhập thuộc tính nội dung văn bản chứa trong ô. getSelectedText(): Trả về chuỗi văn bản được bôi đen (đánh dấu chọn) trong ô. getSelectedStart(): Trả về vị trí kí tự đầu trong vùng được đánh dấu chọn (tính từ 0). getSelectedEnd(): Trả về vị trí kí tự cuối trong vùng được đánh dấu chọn (tính từ 0). selectAll(): Đánh dấu chọn toàn văn bản. setEditable(boolean): Xác định vùng văn bản có thể edit được hay không. Các phương thức khác của lớp TextField: TextField(): Khởi tạo một ô văn bản rỗng. TextField(int): Khởi tạo một ô văn bản rỗng, độ rộng xác định bởi tham số vào. TextField(String): Khởi tạo một ô văn bản có nội dung xác định bởi tham số đầu vào. TextField(String, int): Khởi tạo vởi nội dung có sẵn, độ rộng xác định. setEchoChar(char)/getEchoChar(): Truy nhập thuộc tính là kí tự thay thế văn bản trong ô. Thuộc tính này được dùng khi ta cần che dấu thông tin văn bản, ví dụ, ô gõ mật khẩu của chương trình. getColums(): Trả về độ rộng của ô văn bản. Các phương thức khác của lớp TextArea: TextArea(): KhởiPTIT tạo một vùng văn bản rỗng. TextArea(int, int): Khởi tạo một vùng văn bản rỗng, kích cỡ (số dòng, số cột) xác định bởi tham số vào. TextArea(String): Khởi tạo một vùng văn bản có nội dung xác định bởi tham số đầu vào. TextArea(String, int, int): Khởi tạo vùng văn bản với nội dung có sẵn, độ rộng xác định. appendText(String): Thêm một đoạn văn bản vào cuối đoạn văn bản trong vùng. insertText(String, int): Chèn một đoạn văn bản vào vị trí xác định (tham số thứ hai) của vùng văn bản. replaceText(String, int, int): Thay thế một đoạn văn bản trong vùng, đánh dấu bằng vị trí bắt đầu và vị trí kết thúc (tham số thứ hai và thứ ba), bằng một đoạn văn bản mới (tham số thứ nhất). 140
  7. getRows()/getColums(): Trả về số dòng/cột của vùng văn bản. Chương trình 6.5 minh hoạ việc đặt các đối tượng ô văn bản và vùng văn bản vào một frame. Chương trình 6.5 package vidu.chuong6; import java.awt.*; public class TextDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has some texts!”); myFrame.setSize(300,150); // Định kích cỡ frame // Khai báo và khởi tạo textField TextField myTextField = new TextField(“A text field!”); myFrame.add(myTextField); // Gắn vào frame // Khai báo và khởi tạo textArea TextArea myTextArea = new TextArea(5, 40); String str=“The TextField’s columns is: ”+myTextField.getColumns(); str += “The TextArea’s size is: ” + myTextArea.getRows() + “*” + myTextArea.getColumPTITns(); myTextArea.setText(str); // Thiết lập nội dung myFrame.add(myTextArea); // Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.5: Kết quả demo Text 141
  8. Button Button là đối tượng nút lệnh, dùng để thực hiện một nhiệm vụ xác định. Các phương thức cơ bản của nút nhấn: Button(String): Khởi tạo nút nhấn với tên xác định trên nút. setLabel(String)/getLabel(): Truy nhập tên của nút nhấn. Chương trình 6.6 minh hoạ việc tạo một nút nhấn trong một frame. Chương trình 6.6 package vidu.chuong6; import java.awt.*; public class ButtonDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has a button!”); myFrame.setSize(300,150); // Định kích cỡ frame // Khai báo và khởi tạo button Button myButton = new Button(“Click!”); myFrame.add(myButton); // Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } } PTIT Hình 6.6: Kết quả demo Button Tuy nhiên, khi click vào nút nhấn này, không xảy ra điều già cả. Lí do là chúng ta chưa cài đặt việc xử lí sự kiện cho nút nhấn. Nội dung phần 6.1.3 sẽ trình bày việc xử lí sự kiện cho các đối tượng. 6.1.3 Các sự kiện cơ bản của đối tượng Mỗi đối tượng component có một số sự kiện xác định, phát sinh từ chính đối tượng đó. Java cung cấp một số lớp sự kiện cơ bản nằm trong thư gói java.awt.event: import java.awt.event.*; 142
  9. Các lớp sự kiện cơ bản của các đối tượng bao gồm: ActionEvent: Xuất hiện khi một nút bị click vào, một danh sách (list) được chọn, một menu được chọn. ComponentEvent: Xuất hiện khi một component bị thay đổi kích cỡ, vị trí, trạng thái. FocusEvent: Xuất hiện khi một component có hoặc mất focus. ItemEvent: Xuất hiện khi một menu item được chọn hoặc bỏ, khi checkbox hoặc list item được click vào. WindowEvent: Xuất hiện khi một của sổ được mở ra, kích hoạt, đóng lại hoặc thoát ra. TextEvent: Xuất hiện khi giá trị văn bản của các đối tượng TextField và TextArea bị thay đổi. MouseEvent: Xuất hiện khi chuột được click, di chuyển qua, nhấn xuống và thả ra. KeyEvent: Xuất hiện khi có đầu vào từ bàn phím. Các giao tiếp được cài đặt để xử lí các sự kiện trên: ActionListener. ComponentListener FocusListener ItemListener WindowListener PTIT TextListener MouseListener và MouseMotionListener KeyListener Khi cài đặt các giao tiếp này, cần cài đặt lại phương thức xử lí sự kiện: public void actionPerformed( ){ // Cài đặt lại mã lệnh } Để xác định sự kiện phát sinh từ component nào, ta dùng phương thức getSource(): .getSource(); Chương trình 6.7 cài đặt một ứng dụng hoàn chỉnh, bao gồm: Hai nhãn tiêu đề cho hai ô văn bản. Hai ô văn bản, để nhập số liệu vào. 143
  10. Bốn nút nhấn tương ứng để thực hiện các thao tác nhân, chia, cộng, trừ các số liệu nhập từ hai ô văn bản. Thêm một nút nhấn, khi click vào sẽ thoát khỏi chương trình (chương trình kết thúc). Chương trình 6.7 package vidu.chuong6; import java.awt.*; import java.awt.event.*; public class EventDemo extends Frame implements ActionListener{ Label lbl1, lbl2, lblKq; TextField txt1, txt2; Button btnCong, btnTru, btnNhan, btnChia, btnThoat; public EventDemo(){ super(“Event demo!”); this.setLayout(new GridLayout(6,2)); //Chế độ hiển thị 6 dòng, 2 cột lbl1 = new Label(“So thu nhat:”); // Nhãn số thứ nhất this.add(lbl1); txt1 = new TextField(); // Ô văn bản số thứ nhất this.add(txt1); lbl2 = new Label(“SoPTIT thu hai:”); // Nhãn số thứ hai this.add(lbl2); txt2 = new TextField(); // Ô văn bản số thứ hai this.add(txt2); lblKq = new Label(); // Nhãn kết quả this.add(lblKq); this.add(new Label()); // Các nút nhấn btnCong = new Button(“Cong”); // Nút cộng btnCong.addActionListener(this); // Bắt sự kiện this.add(btnCong); btnTru = new Button(“Tru”); // Nút trừ btnTru.addActionListener(this); this.add(btnTru); 144
  11. btnNhan = new Button(“Nhan”); // Nút nhân btnNhan.addActionListener(this); this.add(btnNhan); btnChia = new Button(“Chia”); // Nút chia btnChia.addActionListener(this); this.add(btnChia); btnThoat = new Button(“Thoat”); // Nút thoát btnThoat.addActionListener(this); this.add(btnThoat); // Phương thức bắt sự kiện click vào nút đóng frame this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } /* Phương thức xử lí sự kiện nút được nhấn */ public void actionPerformed(ActionEvent ae){ float x = Float.parseFloat(txt1.getText()); float y = Float.parseFloat(txt2.getText()); float kq = 0; if(ae.getSource() ==PTIT btnCong) // Cộng hai số kq = x + y; if(ae.getSource() == btnTru) // Trừ hai số kq = x - y; if(ae.getSource() == btnNhan) // Nhan hai số kq = x*y; if(ae.getSource() == btnChia)&&(y != 0) // Chia hai số kq = x/y; if(ae.getSource() == btnThoat) // Thoát khỏi chương trình System.exit(0); // Thay đổi nội dung kết quả lblKq.setText(“Ket qua la: ” + String.valueOf(kq)); } 145
  12. public static void main(String[] args) { // Khai báo đối tượng demo EventDemo myFrame = new EventDemo(); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } 6.2 GIAO DIỆN VỚI CÁC ĐỐI TƯỢNG MULTIMEDIA Nội dung phần này sẽ tập trung trình bày các đối tượng multimedia, bao gồm: Ô đánh dấu (Checkbox) và Nút chọn (Radio button) Lựa chọn (Choice) Danh sách (List) Trình đơn (Menu) 6.2.1 Ô đánh dấu và nút chọn Checkbox và Radio button là các đối tượng dùng để đánh dấu, hoặc chọn thông tin. Sự khác biệt giữa chúng là checkbox cho phép chọn đồng thời nhiều ô cùng lúc, trong khi đó, trong mỗi nhóm radio button, chỉ cho phép chọn một thông tin. Phương thức chung của hai lớp này: setState(boolean)/getState(): Truy nhập đến trạng thái của nút. Các phương thức khởi tạo Checkbox: Checkbox(): Khởi tạo một ô đánh dấu rỗng. Checkbox(String): PTIT Khởi tạo ô đánh dấu có nhãn xác định. Checkbox(String, boolean): Khởi tạo ô đánh dấu có nhãn, có trạng thái xác định. Các phương thức khởi tạo Radio button tương tự như Checkbox, ngoại trừ việc phải chỉ ra nhóm của các radio button: Checkbox(String, boolean, CheckboxGroup); Checkbox(String, CheckboxGroup, boolean); Xử lí sự kiện thay đổi trạng thái nút chọn: Kiểu sự kiện: ItemEvent Cài đặt giao tiếp: ItemListener Phương thức xủa lí: itemStateChange(ItemEvent) Chương trình 6.8 minh hoạ việc dùng một nhóm radio button gồm ba nút, tương ứng với ba màu (RED, BLUE, GREEN). Khi click vào nút nào, thì màu nền sẽ đổi theo màu đó. 146
  13. Chương trình 6.8 package vidu.chuong6; import java.awt.*; import java.awt.event.*; public class RadioDemo extends Frame implements ItemListener{ Checkbox cbxRed, cbxBlue, cbxGreen; public RadioDemo(){ super(“Radio demo!”); //Chế độ hiển thị 3 dòng, 1 cột this.setLayout(new GridLayout(3,1)); CheckboxGroup cbxg = new CheckboxGroup(); // Nhóm radio cbxRed = new Checkbox(“Red”, cbxg, true); // Nút red cbxRed.addItemListener(this); // Bắt sự kiện this.add(cbxRed); cbxBlue = new Checkbox(“Blue”, cbxg, false);// Nút blue cbxBlue.addItemListener(this); // Bắt sự kiện this.add(cbxBlue); cbxGreen = new Checkbox(“Green”, cbxg, false);// Nút green cbxGreen.addItemListener(this); // Bắt sự kiện this.add(cbxGreen); // Phương thức bắt sự kiện click vào nút đóng frame this.addWindowListener(newPTIT WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } /* Phương thức xử lí sự kiện thay đổi trạng thái nút */ public void itemStateChange(ItemEvent ie){ if(ie.getStateChanged() == ItemEvent.SELECTED){ String item = (String)ie.getItem(); if(item.equals(“Red”)) // Đổi màu red this.setBackground(Color.red); if(item.equals(“Blue”)) // Đổi màu blue 147
  14. this.setBackground(Color.blue); if(item.equals(“Green”)) // Đổi màu green this.setBackground(Color.green); this.repaint(); // Vẽ lại màu nền } } public static void main(String[] args) { // Khai báo đối tượng demo RadioDemo myFrame = new RadioDemo(); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.7: Kết quả demo Radio Button 6.2.2 Lựa chọn Choice là đối tượng menu sổ xuống,PTIT hiển thi một danh sách các item và cho phép người dùng chọn một trong số các item đó (tương tự đối tượng dropdown list của window). Các phương thức cơ bản của lớp Choice: Choice(): Khởi tạo đối tượng choice. addItem(String): Thêm một item vào danh sách lựa chọn. remove(int): Xoá item ở vị trí thứ i trong danh sách (bắt đầu là vị trí 0). removeAll(): Xoá toàn bộ item trong danh sách chọn. select(int)/select(String): Chọn một item theo số thứ tự hoặc theo tên. getSelectedIndex(): Trả về chỉ số của item được chọn. getSelectedItem(): Trả về tên của item được chọn. getItem(int): Trả về tên của item tương ứng với số thứ tự đưa vào. Xử lí sự kiện thay đổi trạng thái nút chọn: Kiểu sự kiện: ItemEvent 148
  15. Cài đặt giao tiếp: ItemListener Phương thức xủa lí: itemStateChange(ItemEvent) Chương trình 6.9 có chức năng tương tự như chương trình 6.8: Thay đổi màu nền theo màu được chọn. Nhưng trong chương trình này, ta dùng đối tượng choice, thay vì dùng radio button. Chương trình 6.9 package vidu.chuong6; import java.awt.*; import java.awt.event.*; public class ChoiceDemo extends Frame implements ItemListener{ Choice myChoice; public ChoiceDemo(){ super(“Choice demo!”); myChoice = new Choice(); // Khởi tạo myChoice.addItem(“Red”); // Thêm item red myChoice.addItem(“Blue”); // Thêm item blue myChoice.addItem(“Green”); // Thêm item green myChoice.addItemListener(this); // Bắt sự kiện this.add(myChoice); // Gắn vào frame // Phương thức bắt PTITsự kiện click vào nút đóng frame this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } /* Phương thức xử lí sự kiện thay đổi trạng thái item */ public void itemStateChange(ItemEvent ie){ if(ie.getStateChanged() == ItemEvent.SELECTED){ String item = (String)ie.getItem(); if(item.equals(“Red”)) // Đổi màu red this.setBackground(Color.red); 149
  16. if(item.equals(“Blue”)) // Đổi màu blue this.setBackground(Color.blue); if(item.equals(“Green”)) // Đổi màu green this.setBackground(Color.green); this.repaint(); // Vẽ lại màu nền } } public static void main(String[] args) { // Khai báo đối tượng demo ChoiceDemo myFrame = new ChoiceDemo(); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.8: Kết quả demo Choice Button 6.2.3 Danh sách PTIT List là một danh sách hoạt động tương tự đối tượng choice. Tuy nhiên, list cho phép người dùng có thể chọn một hoặc nhiều item cùng một lúc. Các phương thức cơ bản của lớp List: List(): Khởi tạo một danh sách rỗng, mỗi lần chỉ được chọn một item. List(int): Tương tự, nhưng có qui định số dòng được nhìn thấy. List(int, boolean): Khởi tạo một danh sách có số dòng được nhìn thấy xác định, chế độ cho phép chọn một hay nhiều item xác định bởi tham số thứ hai. add(String): Thêm một item vào danh sách. add(String, int): Chèn một item vào vị trí xác định trong danh sách. Nếu chỉ số chèn vượt ra khỏi phạm vi danh sách, item sẽ được thêm vào cuối. 150
  17. replaceItem(String, int): Thay thế một item ở vị trí xác định (tham số thứ hai) trong danh sách bằng một item mới (tham số thứ nhất). remove(int): Xoá item ở vị trí xác định trong danh sách. removeAll(): Xoá toàn bộ item hiện có của danh sách. getSeletedIndex(): Trả về index của item được chọn (danh sách đơn chọn). getSelectedItem(): Trả về item được chọn (danh sách đơn chọn). getSelectedIndexs(): Trả về chỉ số các item được chọn (danh sách đa chọn). getSelectedItems(): Trả về các item được chọn (danh sách đa chọn). Xử lí sự kiện khi thay đổi item được chọn: Kiểu sự kiện: ItemEvent Cài đặt giao tiếp: ItemListener Phương thức xử lí: itemStateChange(ItemEvent); Chương trình 6.10 minh hoạ việc sử dụng đối tượng list với khả năng đa chọn. Mỗi khi thay đổi item được chọn, một thông báo các màu được chọn sẽ hiện ra. Chương trình 6.10 package vidu.chuong6; import java.awt.*; import java.awt.event.*; public class ListDemo extends Frame implements ItemListener{ List myList; Label lbl; PTIT public ListDemo(){ super(“List demo!”); // Khởi tạo list đa chọn, chỉ nhìn được một dòng myList = new List(1, true); myList.setSize(300,150); // Thêm các item là các loại màu sắc myList.add(“White”); myList.add(“Red”); myList.add(“Orange”); myList.add(“Green”); myList.add(“Yellow”); myList.add(“Blue”); 151
  18. myList.add(“Black”); myList.addItemListener(this); // Bắt sự kiện this.setLayout(new FlowLayout()); this.add(myList); // Gắn vào frame lbl = new Label(); // Khởi tạo nhãn this.add(lbl); // Gắn vào frame // Phương thức bắt sự kiện click vào nút đóng frame this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } /* Phương thức xử lí sự kiện thay đổi trạng thái item */ public void itemStateChange(ItemEvent ie){ if((ie.getStateChange() == ItemEvent.SELECTED)|| (ie.getStateChange() == ItemEvent.DESELECTED)){ String kq = “Cac mau duoc chon:”; String[] items = myList.getSelectedItems(); for(int i=0; i<items.length; i++) kq += items[i] + “, ”; lbl.setText(kq); } PTIT } public static void main(String[] args) { // Khai báo đối tượng demo ListDemo myFrame = new ListDemo(); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } 152
  19. Hình 6.9: Kết quả demo Listbox 6.2.4 Trình đơn Trình đơn (menu) được dùng trên các thanh công cụ của các cửa sổ hoặc là popup menu xuất hiện khi ta click chuột phải vào một đối tượng. Java cung cấp một số lớp trình đơn: Menubar: Thanh trình đơn Menu: Trình đơn đổ xuống PopupMenu: Trình đơn xuất hiện khi click chuột phải. MenuItem: Các mục chọn của trình đơn. Menubar Menubar là thanh công cụ dùng để chứa các trình đơn menu. Các phương thức cơ bản của lớp Menubar: Menubar(): Khởi tạo một thanh công cụ cho trình đơn add(Menu): Thêm một trình đơn mune lên thanh trình đơn menubar. Để đặt một menubar của một frame, ta gọi phương thức của frame: .setMenuBar( ); Menu và PopupMenu Trình đơn menu là đối tượng sẽ sổ xuổng khi click chuột lên đối tượng hiển thị của menu. Menu còn được gọi là menu con của một thanh trình đơn. Các phương thức cơ bản của lớp Menu: Menu(String): Khởi tạoPTIT một menu, với tên xác định. add(MenuItem): Thêm một item vào menu add(Menu): Thêm một menu con vào menu đã có, dùng khi muốn tạo menu có nhiều mức. addSeparator(): Thêm một đường phân vùng vào menu (để nhóm các item với nhau). Xử lí sự kiện của lớp Menu: Kiểu sự kiện: ActionEvent Giao tiếp cài đặt: ActionListener Phương thức xử lí: actionPerformed(ActionEvent); MenuItem MenuItem là đối tượng item trong các trình đơn menu. Mỗi item, khi được click vào sẽ có tác dụng như một nút lệnh. Các phương thức cơ bản của lớp MenuItem: MenuItem(String): Khởi tạo một item. 153
  20. CheckboxMenuItem(String): Khởi tạo một item có mục chọn như checkbox. getState(): Trả về trạng thái của item. Chỉ dùng cho item có mục chọn. enable(): Cho phép item hoạt động (là chế độ mặc định). disable(): Không cho phép item hoạt động (làm mờ item đi). Xử lí sự kiện của lớp MenuItem: Kiểu sự kiện: ActionEvent Giao tiếp cài đặt: ActionListener Phương thức xử lí: actionPerformed(ActionEvent); Chương trình 6.11 minh hoạ việc sử dụng các loại menu: Tạo một menubar của frame Trên menubar, tạo một menu “File”. Khi click vào sẽ sổ xuống một menu với các item: New, Open, Save, Save As, Exit. Khi bấm chuột phải vào frame, sẽ sổ ra một menu popup gồm các item: Cut, Copy, Paste. Khi click chuột vào item nào trên các menu, một nhãn trong frame sẽ hiển thị tên của item vừa được chọn. Chương trình kết thúc khi click vào item Exit. Chương trình 6.11 package vidu.chuong6; import java.awt.*; PTIT import java.awt.event.*; public class MenuDemo extends Frame implements ActionListener, MouseListener{ Menubar myBar; Menu myMenu; PopupMenu myPopup; Label lbl; public MenuDemo(){ super(“Menu demo!”); myBar = new Menubar(); // Thanh trình đơn this.setMenuBar(myBar); // Thiết lập menubar của frame myMenu = new Menu(“File”); // menu File 154
  21. myBar.add(myMenu); // Gắn menu vào thanh trình đơn myMenu.addActionListener(this);// Bắt sự kiện myMenu.add(new MenuItem(“New”)); myMenu.add(new MenuItem(“Open”)); myMenu.addSeparator(); // Thêm dấu nhóm item myMenu.add(new MenuItem(“Save”)); myMenu.add(new MenuItem(“Save As”)); myMenu.addSeparator(); myMenu.add(new MenuItem(“Exit”)); myPopup = new PopupMenu(“Options”);// Menu popup myPopup.addActionListener(this);// Bắt sự kiện myPopup(new MenuItem(“Cut”)); myPopup(new MenuItem(“Copy”)); myPopup(new MenuItem(“Paste”)); lbl = new Label(); // Khởi tạo nhãn this.add(lbl); // Gắn vào frame // Phương thức bắt sự kiện click vào nút đóng frame this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0);PTIT } }); } /* Phương thức xử lí sự kiện */ public void actionPerformed(ActionEvent ae){ if(ae.getActionCommand().equals(“Exit”)){ System.exit(0); } lbl.setText(ae.getActionCommand()); } public void mouseEntered(MouseEvent me){} // Không xử lí public void mouseExited(MouseEvent me){} // Không xử lí 155
  22. public void mouseReleased(MouseEvent me){} // Không xử lí public void mousePressed(MouseEvent me){} // Không xử lí public void mouseClicked(MouseEvent me){ myPopup.show(this, me.getX(), me.getY());// Hiện menu popup } public static void main(String[] args) { // Khai báo đối tượng demo MenuDemo myFrame = new MenuDemo(); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.10: Kết quả demo Menu 6.3 CÁC KỸ THUẬT TẠO TABLES Nội dung phần này sẽ tập trungPTIT trình bày các kỹ thuật trình bày các đối tượng giao diện (conponent) trên frame theo các ý đồ thiết kế khác nhau bằng cách dùng bộ quản lí trình bày (Layout Manager). Bao gồm các kỹ thuật sau: Cách trình bày theo dòng (Flow layout) Cách trình bày theo mảng (Grid layout) Cách trình bày theo Border (Border layout) Cách trình bày theo GridBag (GridBag layout) Cách trình bày tự do (Null layout) 6.3.1 Trình bày Flow Layout Cách trình bày Flow Layout sẽ xếp các đối tượng trên một hướng theo dòng. Nếu đối tượng mới thêm không đủ chỗ (chiều rộng) thì nó sẽ tự động thêm vào đầu dòng mới. Các phương thức: FlowLayout(): Khởi tạo đối tượng trình bày. FlowLayout(int): Khởi tạo đối tượng trình bày với cách căn lề xác định. 156
  23. FlowLayout(int, int, int): Khởi tạo với ba tham số: Thứ nhất là cách căn lề, thứ hai là khoảng cách giữa hai dòng (chiều cao), thứ ba là khoảng cách giữa hai đối tượng (chiều ngang). Tham số căn lề có thể nhận một trong ba giá trị: FlowLayout.LEFT: Căn lề trái, là giá trị mặc định. FlowLayout.CENTER: Căn lề giữa. FlowLayout.RIGHT: Căn lề phải. Chương trình 6.12 minh hoạ cách trình bày flow layout: Tạo ra một dãy 10 nút nhấn và gắn vào một frame theo kiểu flow layout. Chương trình 6.12 package vidu.chuong6; import java.awt.*; public class FlowLayoutDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has somes buttons!”); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setLayout(new FlowLayout( ));// Thiết lập cách trình bày // Khai báo và khởi tạo button for(int i=0; i<10; PTITi++) myFrame.add(new Button(“Click”+i));// Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.11: Kết quả demo Flow layout 157
  24. 6.3.2 Trình bày Grid Layout Cách trình bày Grid Layout sẽ sắp xếp các đối tượng theo dạng bảng, được xác định số hàng và số cột. Phương thức cơ bản: GridLayout(int, int): Khởi tạo một đối tượng trình bày. Hai tham số đầu vào lần lượt là số hàng và số cột của grid trình bày. GridLayout(int, int, int, int): Khởi tạo một đối tượng trình bày, hai tham số đầu xác định số hàng và số cột trình bày. Hai tham số sau xác định khoảng cách giữa các dòng và các cột của bảng. Lưu ý: Khi số lượng đối tượng được chèn nhiều hơn vào frame, ta muốn chương trình tự tính số hàng, hoặc tự tính số cột hiển thị, thì ta để tham số tương ứng là 0. Ví dụ: setLayout(new GridLayout(3,0)); sẽ cố định số hàng trình bày là 3, số cột là tuỳ thuộc vào số đối tượng trong frame. setLayout(new GridLayout(0,2)); sẽ cố định số cột là 2, số dòng là mềm dẻo theo số các đối tượng trong frame. Chương trình 6.13 minh hoạ cách trình bày grid layout: Tạo ra một dãy 10 nút nhấn và gắn vào một frame theo kiểu grid layout. Chương trình 6.13 package vidu.chuong6; PTIT import java.awt.*; public class GridLayoutDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has somes buttons!”); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setLayout(new GridLayout(0,2));// Thiết lập cách trình bày // Khai báo và khởi tạo button for(int i=0; i<10; i++) myFrame.add(new Button(“Click”+i));// Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } 158
  25. } Hình 6.12: Kết quả demo Grid layout 6.3.3 Trình bày Border Layout Cách hiển thị Border Layout sẽ chia frame thành 5 vùng cố định và tự động kéo dãn các vùng sao cho chiếm hết bề mặt của frame: West: Vùng phía tây, tức là phía lề bên trái. East: Vùng phía đông, tức là phía lề bên phải. North: Vùng phía bắc, tức là phía lề trên. South: Vùng phía nam, tức là phía lề dưới. Center: Vùng trung tâm, ở chính giữa frame. Phương thức cơ bản của lớp BorderLayout: BorderLayout(): Khởi tạo một đối tượng trình bày theo cách border. Khi một frame được trình bày theo cách border, ta có thể dùng phương thức sau để gắn các đối tượng vào các vùng của frame: .add( , ); Ví dụ: PTIT myFrame.add(“Center”, new Button(“Click”)); sẽ gán vào vùng trung tâm của myFrame một nút nhấn có tên là “Click”. Lưu ý: Cách trình bày border luôn chia frame thành 5 vùng xác định. Nếu gắn nhiều đối tượng vào cùng một vùng, chỉ có đối tượng gắn sau là nhìn thấy được. Nếu muốn trong một vùng chứa được nhiều đối tượng, ta có thể gắn vào mỗi vùng một Panel. Sau đó trong panel, ta chọn cách trình bày riêng cho panel và gắn các đối tượng vào panel. Chương trình 6.14 minh hoạ cách trình bày border: Ta sẽ gắn vào năm vùng của frame năm nút nhấn khác nhau. 159
  26. Chương trình 6.14 package vidu.chuong6; import java.awt.*; public class BorderLayoutDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has somes buttons!”); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setLayout(new BorderLayout()); // Định cách trình bày // Khai báo và khởi tạo button myFrame.add(“West”, new Button(“West”)); // Gắn vào vùng west myFrame.add(“East”, new Button(“East”)); // Gắn vào vùng east myFrame.add(“North”, new Button(“North”)); // Gắn vào vùng north myFrame.add(“South”, new Button(“South”)); // Gắn vào vùng south // Gắn vào vùng center myFrame.add(“Center”, new Button(“Center”)); myFrame.setVisible(true); // Hiển thị frame } PTIT } Hình 6.13: Kết quả demo Border layout 6.3.4 Trình bày GridBag Layout Cách trình bày GridBag Layout cũng trình bày các đối tượng tương tự như Grid Layout: Các đối tượng sẽ được định vị theo vị trí các ô (cell) của một khung lưới (grid). Tuy nhiên, GridBag cho phép ta định kích thước của đối tượng sẽ chiếm bao nhiêu ô và sẽ được đặt ở vị trí nào trong khung lưới. Các phương thức cơ bản: 160
  27. GridBagLayout(): Khởi tạo một đối tượng trình bày theo cách gridbag. setConstraints(Component, GridBagConstraints): Đặt vị trí và kích thước của đối tượng component theo các ràng buộc trong gridbagConstraints. GridBagConstraints Đây là lớp chứa các ràng buộc cho các đối tượng được trình bày theo cách GridBag. Các phương thức và thuộc tính cơ bản của lớp GridBagConstraints: GridBagConstraints(): Khởi tạo một đối tượng ràng buộc của GridBag. gridx/gridy: Vị trí của cell mà ta muốn đặt đối tượng vào (theo chiều X và chiều Y). gridwidth/gridheight: Kích thước (vùng trình bày) của đối tượng (Theo chiều rộng và chiều cao). fill: Xác định cách đặt đối tượng, theo 4 cách: - GridBagConstraints.NONE: Đối tượng không thay đổi kích thước theo các cell nó chiếm. - GridBagConstraints.VERTICAL: Đối tượng có chiều cao kín vùng nó chiếm - GridBagConstraints.HORIZONAL: Đối tượng có chiều rộng kín vùng nó chiếm - GridBagConstraints.BOTH: Đối tượng có chiều cao và chiều rộng phủ kín vùng nó chiếm. ipadx/ipady: ĐịnhPTIT đơn vị tăng giảm kích thước của đối tượng khi khung chứa bị thay đổi kích thước (theo chiều X và chiều Y). insets: Xác định khoảng cách giữa các cell theo bốn hướng: Trên, dưới, trái, phải. anchor: Xác định vị trí neo đối tượng khi kích thước khung chứa thay đổi. Bao gồm: NORTH, NORTHEAST, NORTHWEST, EAST, SOUTH, SOUTHEAST, SOUTHWEST. weightx/weighty: Định khoảng cách lớn ra tương đối giữa các đối tượng với nhau. Chương trình 6.15 minh hoạ cách trình bày gridbag: Tạo ra ba nút nhấn trong frame, mỗi nút có một số ràng buộc khác nhau về kích thước và vị trí. Chương trình 6.15 package vidu.chuong6; 161
  28. import java.awt.*; public class GridBagLayoutDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has somes buttons!”); myFrame.setSize(300,150); // Định kích cỡ frame GridBagLayout layout = new GridBagLayout(); myFrame.setLayout(layout); // Định cách trình bày // Khai báo đối tượng ràng buộc GridBagConstraints cts = new GridBagConstraints(); cts.fill = GridBagConstraints.BOTH; // Button1: vị trí (1,1), kích thước (1,1) Button btn1 = new Button(“Click1”); cts.gridx = 1; cts.gridy = 1; cts.gridheight = 1; cts.gridwidth = 1; layout.setConstraints(btn1, cts); // Định ràng buộc myFrame.add(btn1); // Gắn vào frame // Button2: vị trí (2,2), kích thước (1,1) Button btn2 = new Button(“Click2”);PTIT cts.gridx = 2; cts.gridy = 2; cts.gridheight = 1; cts.gridwidth = 1; layout.setConstraints(btn2, cts); // Định ràng buộc myFrame.add(btn2); // Gắn vào frame // Button3: vị trí (3,3), kích thước (1,1) Button btn3 = new Button(“Click3”); cts.gridx = 3; cts.gridy = 3; cts.gridheight = 1; cts.gridwidth = 1; 162
  29. layout.setConstraints(btn3, cts); // Định ràng buộc myFrame.add(btn3); // Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.14: Kết quả demo Gridbag layout 6.3.5 Trình bày Null Layout Cách trình bày Null Layout sẽ trình bày các đối tượng không theo một quy tắc nào. Tất cả đều do người dùng tự định vị và thiết lập kích thước cho mỗi đối tượng. Định vị đối tượng bằng phương thức setLocation(): .setLocation(Point); Định kích thước đối tượng bằng phương thức setSize(): .setSize(int, int); Ngoài ra, có thể vừa định vị, vừa định kích thước cho đối tượng thông qua phương thức: .setBounds(int,PTIT int, int, int); Trong đó, hai tham số dầu định vị đối tượng, hai tham số sau định kích thước đối tượng. Chương trình 6.16 minh hoạ cách trình bày tự do Null layout: tạo ra hai nút nhấn và gắn vào frame theo hai cách khác nhau. Chương trình 6.16 package vidu.chuong6; import java.awt.*; public class NullLayoutDemo{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề Frame myFrame = new Frame(“Frame has somes buttons!”); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setLayout(null); // Định cách trình bày 163
  30. // Button1: vị trí (10,30), kích thước (100,40) Button btn1 = new Button(“Click1”); btn1.setSize(100, 40); btn1.setLocation(new Point(10, 30)); myFrame.add(btn1); // Gắn vào frame // Button2: vị trí (70,120), kích thước (50,20) Button btn2 = new Button(“Click2”); btn2.setBounds(70, 120, 50, 20); myFrame.add(btn2); // Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } } Hình 6.15: Kết quả demo Null layout 6.4 HTML & APPLET PTIT Applet là một chương trình Java có thể chạy trong các trình duyệt web có hỗ trợ Java. Tất cả các applet đều là các lớp con của lớp Applet. Để tạo applet, ta cần import hai gói sau: import java.applet.*; import java.awt.*; 6.4.1 Cấu trúc của một Applet Cấu trúc tổng quát của một applet như sau: public class extends Applet{ // Các thuộc tính public void init(){ } public void start(){ } public void stop(){ } public void destroy(){ } // Các phương thức khác } 164
  31. Các phương thức cơ bản của một applet: init(): Khởi tạo các tham số, nếu có, của applet. start(): Applet bắt đầu hoạt động. stop(): Chấm dứt hoạt động của applet. destroy(): Thực hiện các thao tác dọn dẹp trước khi thoát khỏi applet. Lưu ý: Không phải tất cả các applet đều phải cài đặt đầy đủ 4 phương thức cơ bản trên. Applet còn có thể cài đặt một số phương thức tuỳ chọn (không bắt buộc) sau: paint(Graphics): Phương thức vẽ các đối tượng giao diện bên trong applet. Các thao tác vẽ này được thực hiện bởi đối tượng đồ hoạ Graphics (là tham số đầu vào). repaint(): Dùng để vẽ lại các đối tượng trong applet. Phương thức này sẽ tự động gọi phương thức update(). update(Graphics): Phương thức này được gọi sau khi thực hiện phương thức paint nhằm tăng hiệu quả vẽ. Phương này sẽ tự động gọi phương thức paint(). Chương trình 6.17 cài đặt một applet đơn giản, mỗi phương thức sẽ in ra thông báo rằng applet đang ở trong thời điểm tương ứng. Chương trình 6.17 package vidu.chuong6; import java.awt.*; PTIT import java.applet.*; public class SimpleApplet extends Applet{ private StringBuffer buffer; // Chuỗi thông báo public void init(){ // Khởi tạo buffer = new StringBuffer(); addBuffer(“initializing ”); } public void start(){ // Kích hoạt addBuffer(“starting ”); } public void stop(){ // Dừng addBuffer(“stopping ”); 165
  32. } public void destroy(){ // Thoát addBuffer(“unloading ”); } private void addBuffer(String newBuffer){ buffer.append(newBuffer); // Thêm thông báo repaint(); } public void paint(Graphics g){ g.drawString(buffer.toString(), 5, 15); // Hiện thông báo } } 6.4.2 Sử dụng applet Applet không thể chạy như một ứng dụng Java độc lập (nó không có hàm main), mà nó chỉ chạy được khi được nhúng trong một trang HTML (đuôi .htm, .html) và chạy bằng một trình duyệt web thông thường. Các bước xây dựng và sử dụng một applet bao gồm: Cài đặt chương trình có dạng một applet như mục 6.4.1 Biên dịch mã nguồn thành lớp .class Nhúng mã .class của appletPTIT vào trang html. Để nhúng một applet vào một trang html, ta dùng thẻ (tag) như sau: Trong đó: Tên applet là tên file mã nguồn đã biên dịch thành file chạy có đuôi .class của Java. Chiều rộng và chiều cao là kích thước của vùng trên trang html mà applet sẽ được đặt vào. Ví dụ, trong trang myHtml.htm có chứa nội dung như sau: A simple applet 166
  33. This is the output of applet: sẽ nhúng applet đã được định nghĩa trong chương trình 6.17 vào một vùng có kích thước 200*20 trong trang myHtml. Bây giờ, ta có thể kiểm nghiệm chương trình applet của mình bằng cách mở trang myHtml trên các trình duyệt thông thường. Chương trình 6.18 cài đặt một applet có chức năng tương tự như chương trình 6.7, thực hiện các thao tác tính toán cơ bản trên hai số. Ngoại trừ việc đây là một applet, nên có thể chạy trên một trang html. Chương trình 6.18 package vidu.chuong6; import java.awt.*; import java.awt.event.*; import java.applet.*; public class AppletDemo extends Applet implements ActionListener{ Label lbl1, lbl2, lblKq; TextField txt1, txt2; Button btnCong, btnTru,PTIT btnNhan, btnChia, btnThoat; public void init(){ this.setLayout(new GridLayout(6,2)); //Chế độ hiển thị 6 dòng, 2 cột lbl1 = new Label(“So thu nhat:”); // Nhãn số thứ nhất this.add(lbl1); txt1 = new TextField(); // Ô văn bản số thứ nhất this.add(txt1); lbl2 = new Label(“So thu hai:”); // Nhãn số thứ hai this.add(lbl2); txt2 = new TextField(); // Ô văn bản số thứ hai this.add(txt2); lblKq = new Label(); // Nhãn kết quả this.add(lblKq); 167
  34. this.add(new Label()); // Các nút nhấn btnCong = new Button(“Cong”); // Nút cộng btnCong.addActionListener(this); // Bắt sự kiện this.add(btnCong); btnTru = new Button(“Tru”); // Nút trừ btnTru.addActionListener(this); this.add(btnTru); btnNhan = new Button(“Nhan”); // Nút nhân btnNhan.addActionListener(this); this.add(btnNhan); btnChia = new Button(“Chia”); // Nút chia btnChia.addActionListener(this); this.add(btnChia); btnThoat = new Button(“Thoat”); // Nút thoát btnThoat.addActionListener(this); this.add(btnThoat); } /* Phương thức xử lí sự kiện nút được nhấn */ public void actionPerformed(ActionEvent ae){ float x = Float.parseFloat(txt1.getText()); float y = Float.parseFloat(txt2.getText());PTIT float kq = 0; if(ae.getSource() == btnCong) // Cộng hai số kq = x + y; if(ae.getSource() == btnTru) // Trừ hai số kq = x - y; if(ae.getSource() == btnNhan) // Nhan hai số kq = x*y; if(ae.getSource() == btnChia)&&(y != 0) // Chia hai số kq = x/y; if(ae.getSource() == btnThoat) // Thoát khỏi chương trình System.exit(0); // Thay đổi nội dung kết quả lblKq.setText(“Ket qua la: ” + String.valueOf(kq)); 168
  35. repaint(); // Vẽ lại các đối tượng } } Khi nhúng applet này vào một trang html bất kì, ta có thể kiểm tra thấy rằng nó có chức năng tương tự như ứng dụng 6.7. Lưu ý, sự khác nhau giữa một application và một applet: Application là một ứng dụng Java độc lập, nó có thể chạy độc lập trên máy ảo Java. Trong khi đó, applet chỉ chạy được khi nhúng trong một trang html, chạy nhờ vào các trình duyệt web có hỗ trợ Java. Application chạy dựa vào hàm main(). Trong khi đó, applet không có hàm main(). Để hiển thị các thông báo, application dùng System.out.println(). Trong khi đó, applet dùng phương thức drawString() của lớp Graphics. 6.4.3 Truyền tham số cho Applet Trong nhiều trường hợp, applet phải phụ thuộc vào các tham số ở bên ngoài truyền vào. Khi đó ta có thể dùng thẻ PARAM của html để truyền tham số cho applet. Cú pháp: // Các tham số khác Khi đó, trong mã nguồn của applet,PTIT ta dùng phương thức getParameter() để đọc giá trị các tham số được truyền vào: getParameter(Tên_biến); Chương trình 6.19 minh hoạ việc truyền tham số cho một applet: Applet mô phỏng giao diện tìm kiếm: một nhãn hướng dẫn, một ô văn bản và một nút nhấn. Tuỳ vào ngôn ngữ mà nhãn và nút nhấn có giá trị text khác nhau. Biến ngôn ngữ là một tham số truyền từ trình duyệt vào. (Đây là mô phỏng giao diện, cơ chế tìm kiếm không hoạt động). Chương trình 6.19 package vidu.chuong6; import java.awt.*; import java.applet.*; public class ParamDemo extends Applet{ 169
  36. Label lbl; TextField txt; Button btn; public void init(){ this.setLayout(new GridLayout(2,2)); //Chế độ hiển thị 6 dòng, 2 cột String langue = getParameter(“langue”);// Loại ngôn ngữ if(langue.equals(“vn”)){ // Tiếng Việt lbl = new Label(“Nhap tu khoa”); // Nhãn số thứ nhất btn = new Button(“Tim kiem”); // Nút cộng }else if(langue.equals(“fr”)){ // Tiếng Pháp lbl = new Label(“Tapez des mots”); btn = new Button(“Chercher”); }else{ // Tiếng Anh, mặc định lbl = new Label(“Enter keys”); btn = new Button(“Search”); } txt = new TextField(); this.add(lbl); this.add(txt); this.add(btn); } } PTIT Khi đó, applet phải được nhúng vào trang html với đoạn mã như sau: Ta có thể thay thế value của param bằng các giá trị “vn”, “fr” và “en” để thấy được các chế độ ngôn ngữ khác nhau được hiển thị trong applet. 6.5 GIỚI THIỆU VỀ SWING Swing là thư viện lập trình mở rộng của Java. Nó mở rộng các đối tượng giao diện đồ hoạ cơ bản của Java. Swing còn được gọi là thư viện JFC (Java Foundation Class). Khi muốn sử dụng các đối tượng đồ hoạ của thư viện này, ta phải khai báo chỉ thị: import javax.swing.*; 170
  37. 6.5.1 Mở rộng các đối tượng component JFC mở rộng các đối tượng cơ bản của java thành các lớp tương ứng, ngoài trừ việc có thêm chữ “J” ở đầu mỗi tên lớp: Button JButton Label JLabel TextField JTextField TextArea JTextArea Checkbox JCheckbox List JList Menu JMenu Các lớp mở rộng này có đầy đủ các phương thức của các đối tượng lớp cơ bản của thư viện java.awt. Ngoài ra, chúng được bổ sung một số phương thức tạo hiệu ứng giao diện. Chương trình 6.20 minh hoạ việc sử dụng đối tượng JButton. Đối tượng JButton được mở rộng thêm một số tính năng sau: JButton(String, Icon): Khởi tạo một nút nhấn với một tên nhãn và một ảnh nền. Ảnh nền có kiểu icon (tham số thứ hai). setMnemonic(char): Định phím tắt cho nút lệnh. Khi người dùng nhấn “Ctrl+phím tắt” thì nút lệnh cũng thực thi tương tự như kkhi ta click chuột vào nút lệnh. setBorder(new MatteBorder(int, int, int, int, Icon)): Thiết lập khung nền cho nút với các tham số: Khoảng cách từ chữ đến biên (độ rộng biên) theo các chiều trên dưới, trái phải, cuối cùng là ảnh nền cho nút. setBorder(new LineBorder(int)):PTIT Thiết lập viền cho nút dạng hình chữ nhật, tham số xác định màu cho viền của nút. Ngoài ra, tham số của phương thức này còn có thể là các lớp SoftBevelBorder, EtchedBorder và TitleBorder. setToolTipText(String): Thiết lập dòng tooltip cho đối tượng. Dòng này sẽ hiển ra khi ta di chuột lên đối tượng trên cửa sổ. Chương trình 6.20 package vidu.chuong6; import javax.swing.*; public class JButtonDemo extends JFrame{ public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề JFrame myFrame = new JFrame(“Frame has somes buttons!”); myFrame.setSize(300,150); // Định kích cỡ frame 171
  38. // Giả sử ta có file ảnh myImage trong cùng thư mục Icon myIcon = new ImageIcon(“myImage.gif”); // Button1: có nền là ảnh JButton btn1 = new JButton(“Back Image”, myIcon); // Gán tooltip cho nút btn1.setToolTipText(“Button’s background is an image”); myFrame.getContentPane().add(btn1); // Gắn vào frame // Button2: có biên là ảnh JButton btn2 = new JButton(“Border Image”); // Gán tooltip cho nút btn1.setToolTipText(“Button’s border is an image”); btn2.setBorder(new MatteBorder(10,10,10,10, myIcon)); myFrame.getContentPane().add(btn2); // Gắn vào frame myFrame.setVisible(true); // Hiển thị frame } } Trong chương trình này, có dòng lệnh gắn các đối tượng vào frame bằng cách getContentPane(). Đây là phương thức mở rộng cho các đối tượng khung chứa container. Sự mở rộng này sẽ được trình bàyPTIT chi tiết trong phần tiếp theo. 6.5.2 Mở rộng các đối tượng container Tương tự như các đối tượng component, các đối tượng container cũng được mở rộng trong JFC thành các lớp có tên tương ứng và thêm kí tự “J” ở đầu: Frame JFrame Panel JPanel Dialog JDialog Chương trình 6.21 minh hoạ việc sử dụng các đối tượng mở rộng của khung chứa Frame thành JFrame. Khung chứa JFrame có nhiều tầng trình diễn khác nhau, các tầng là trong suốt và chồng khít lên nhau, khiến cho ta vẫn có cảm giác các đối tượng được trình bày trên cùng một mặt phẳng như khung chứa Frame của thư viện chuẩn AWT. Một số tầng hay sử dụng của lớp JFrame (theo thứ tự từ trong ra ngoài): 172
  39. ContentPane: Là tầng thường dùng nhất, tầng này dùng để chứa các đối tượng component cơ bản như button, label, text, list MenubarPane: Tầng dành để chứa các loại menu của frame như Menubar, PopupMenu. GlassPane: Tầng ngoài cùng, thường dùng để chứa các tooltip của các đối tượng trong tầng Content. Khi ta set tooltipText cho một đối tượng, tooltip đó sẽ tự động được add vào tầng Glass. Để truy nhập vào một tầng bất kì, ta dùng phương thức có tên: get + (); Ví dụ: JFrame myFrame = new JFrame(“My JFrame”); myFrame.getContentPane().add(“Center”, new JButton(“Test”)); sẽ gắn một nút nhấn có nhãn Test vào tầng Content của khung chứa myFrame. Chương trình 6.21 minh hoạ việc gắn các đối tượng vào các tầng khác nhau: Gắn một nút nhấn vào tầng ContentPane. Gắn một thanh Menubar có chứa một menu File vào tầng MenubarPane. Chương trình 6.21 package vidu.chuong6; import javax.swing.*; import java.awt.event.*; public class JFrameDemo PTITextends JFrame implements ActionListener{ private JMenuBar myBar; private JMenu myMenu; public JFrameDemo(){ super(“JFrame demo”); JButton btn = new Jbutton(); // Gắn nút nhấn vào tầng ContentPane this.getContentPane().add(“Center”, btn); myBar = new JMenuBar(); myMenu = new JMenu(“File”); myMenu.add(new JMenuItem(“Open”)); myMenu.add(new JMenuItem(“New”)); myMenu.add(new JSeparator()); 173
  40. myMenu.add(new JMenuItem(“Save”)); myMenu.add(new JMenuItem(“Save As”)); myMenu.add(new JSeparator()); myMenu.add(new JMenuItem(“Exit”)); myMenu.addActionListener(this); myBar.add(myMenu); // Gắn menubar vào tầng MenubarPane this.getJMenuBar().add(myBar); // Phương thức bắt sự kiện click vào nút đóng frame this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); } /* Phương thức xử lí sự kiện */ public void actionPerformed(ActionEvent ae){ if(ae.getActionCommand().equals(“Exit”)){ System.exit(0); } } PTIT public static void main(String[] args) { // Khai báo và khởi tạo frame có tiêu đề JFrameDemo myFrame = new JFrameDemo(); myFrame.setSize(300,150); // Định kích cỡ frame myFrame.setVisible(true); // Hiển thị frame } } Lưu ý: Vì các đối tượng mở rộng của thư viện JFC được bổ sung khá nhiều tính năng, đặc biệt là các tính năng đồ hoạ, do đó, các đối tượng này có nhược điểm là rất cồng kềnh. Vì lí do nặng tải, cho nên hiện nay, các đối tượng của thư viện JFC vẫn ít được phổ biến trong các ứng dụng applet. 174
  41. 6.6 CASE STUDY IV Trong phần này, ta sẽ minh hoạ cách sử dụng các đối tượng đồ hoạ của thư viện chuẩn AWT để viết một chương trình mô phỏng một máy tính Calculator. Tạo một frame làm khung chương trình, tiêu đề là “Java Calculator” Phía trên là một Label (hoặc ô văn bản đều được, nhưng nếu dùng ô văn bản thì không cho edit) để hiện các số được nhập vào và kết quả tính toán. Phía dưới là các nút nhấn tương ứng với các chữ số và phép toán. Nhưng để nhóm các nút nhấn cho đồng bộ và layout đẹp mắt, ta nhóm chúng vào một Panel. Khi đó, frame sẽ chứa trực tiếp hai đối tượng: label và frame. Ta sử dụng layout kiểu null, và xác định vị trí chính xác cho label và panel. Đối với Panel, ta cũng dùng GridLayout. Vì có 10 nút nhấn số và các nút nhấn toán tử: nút cộng, nút nhân, nút chia, nút trừ, nút căn bậc hai, nút phẩy thập phân, nút bằng, nút luỹ thừa, nút nghịch đảo, nút reset. Nên sẽ tạo thành 4 dòng, 5 cột: mỗi dòng gồm có 3 nút số và hai nút chức năng: - Dòng 1: các nút 7, 8, 9, cộng, reset (C). - Dòng 2: các nút 4, 5, 6, trừ, luỹ thừa. - Dòng 3: các nút 1, 2, 3, nhân, nghịch đảo. - Dòng 4: các nút 0, thập phân, nút bằng, nút chia, nút căn bậc hai. Với các nút số và nút thập phân, khi click vào nút thì kí tự tương ứng được hiện lên trên label. Với các nút chức năng, khi click vào thì thực hiện phép toán và hiện kết quả ra màn hình, nếu có. PTIT Khi click vào nút bằng (kết quả) thì hiện kết quả trên label. Chương trình 6.22 cài đặt chi tiết chương trình này. Chương trình 6.22 package vidu.chuong6; import java.awt.*; import java.awt.event.*; import java.lang.Math; public class CalculatorDemo extends Frame implements ActionListener{ private boolean operatorState; // Trạng thái của phép toán private int operator; // Toán tử thực hiện private float oldIterator; // Số hạng trước 175
  42. private Label lbl; private Panel pnl; private Button btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btnPoint, btnReset, btnAdd, btnSub, btnMul, btnDiv, btnPow, btnSqrt, btnRev, btnResult; public CalculatorDemo(){ super(“Java Calculator”); this.setSize(250, 250); this.setResizable(false); // Không cho thay đổi size this.setLayout(null); // Thiết lập layout lbl = new Label("0"); // Nhãn kết quả lbl.setAlignment(2); lbl.setSize(240, 30); lbl.setLocation(5, 25); this.add(lbl); Panel pnl = new Panel(); // Panel chứa các nút pnl.setSize(240, 190); pnl.setLocation(5, 55); pnl.setLayout(new GridLayout(4, 5)); // Thiết lập layout this.add(pnl); PTIT btn7 = new Button(“7”); // Nút số 7 btn7.addActionListener(this); // Bắt sự kiện click chuột pnl.add(btn7); // Gắn vào panel btn8 = new Button(“8”); // Nút số 8 btn8.addActionListener(this); pnl.add(btn8); btn9 = new Button(“9”); // Nút số 9 btn9.addActionListener(this); pnl.add(btn9); 176
  43. btnAdd = new Button(“+”); // Nút phép toán cộng btnAdd.addActionListener(this); pnl.add(btnAdd); btnReset = new Button(“C”); // Nút reset btnReset.addActionListener(this); pnl.add(btnReset); btn4 = new Button(“4”); // Nút số 4 btn4.addActionListener(this); pnl.add(btn4); btn5 = new Button(“5”); // Nút số 5 btn5.addActionListener(this); pnl.add(btn5); btn6 = new Button(“6”); // Nút số 6 btn6.addActionListener(this); pnl.add(btn6); btnSub = new Button(“-”); // Nút phép toán trừ btnSub.addActionListener(this); pnl.add(btnSub); PTIT btnPow = new Button(“x^y”); // Nút phép toán luỹ thừa btnPow.addActionListener(this); pnl.add(btnPow); btn1 = new Button(“1”); // Nút số 1 btn1.addActionListener(this); pnl.add(btn1); btn2 = new Button(“2”); // Nút số 2 btn2.addActionListener(this); pnl.add(btn2); btn3 = new Button(“3”); // Nút số 3 177
  44. btn3.addActionListener(this); pnl.add(btn3); btnMul = new Button(“*”); // Nút phép toán nhân btnMul.addActionListener(this); pnl.add(btnMul); btnRev = new Button(“1/x”); // Nút phép toán nghịch đảo btnRev.addActionListener(this); pnl.add(btnRev); btn0 = new Button(“0”); // Nút số 0 btn0.addActionListener(this); pnl.add(btn0); btnPoint = new Button(“.”); // Nút dấu thập phân btnPoint.addActionListener(this); pnl.add(btnPoint); btnResult = new Button(“=”); // Nút kết quả btnResult.addActionListener(this); pnl.add(btnResult); PTIT btnDiv = new Button(“/”); // Nút phép toán chia btnDiv.addActionListener(this); pnl.add(btnDiv); btnSqrt = new Button(“Sqrt”); // Nút phép toán căn bậc hai btnSqrt.addActionListener(this); pnl.add(btnSqrt); operatorState = true; operator = -1; oldIterator = 0; this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ 178
  45. System.exit(0); } }); } /* Phương thức xử lí sự kiện */ public void actionPerformed(ActionEvent ae){ float result; float newIterator = Float.parseFloat(lbl.getText()); if(ae.getSource() == btnResult){ switch(operator){ case 0: result = oldIterator + newIterator; lbl.setText(String.valueOf(result)); break; case 1: result = oldIterator - newIterator; lbl.setText(String.valueOf(result)); break; case 2: result = oldIterator * newIterator; lbl.setText(String.valueOf(result)); break; case 3: PTIT if(newIterator != 0){ result = oldIterator/newIterator; lbl.setText(String.valueOf(result)); } break; case 4: result = (float)Math.pow(oldIterator, newIterator); lbl.setText(String.valueOf(result)); break; } operator = -1; operatorState = true; 179
  46. return; } if(ae.getSource() == btnRev){ newIterator = Float.parseFloat(lbl.getText()); if(newIterator != 0){ result = (float)1/newIterator; lbl.setText(String.valueOf(result)); } operator = -1; operatorState = true; return; } if(ae.getSource() == btnSqrt){ newIterator = Float.parseFloat(lbl.getText()); if(newIterator >= 0){ result = (float)Math.sqrt(newIterator); lbl.setText(String.valueOf(result)); } operator = -1; operatorState = true; return; } if(ae.getSource() == btnPoint){ lbl.setText(lbl.getText()PTIT + “.”); return; } if(ae.getSource() == btnAdd){ operator = 0; operatorState = true; oldIterator = Float.parseFloat(lbl.getText()); return; } if(ae.getSource() == btnSub){ operator = 1; operatorState = true; oldIterator = Float.parseFloat(lbl.getText()); return; 180
  47. } if(ae.getSource() == btnMul){ operator = 2; operatorState = true; oldIterator = Float.parseFloat(lbl.getText()); return; } if(ae.getSource() == btnDiv){ operator = 3; operatorState = true; oldIterator = Float.parseFloat(lbl.getText()); return; } if(ae.getSource() == btnPow){ operator = 4; operatorState = true; oldIterator = Float.parseFloat(lbl.getText()); return; } if(ae.getSource() == btnReset){ operator = -1; operatorState = true; oldIterator = 0; lbl.setText("0");PTIT return; } // Trường hợp click vào nút số if(operatorState){ // Bắt đầu số mới lbl.setText(ae.getActionCommand()); operatorState = false; }else // Gõ tiếp số cũ lbl.setText(lbl.getText() + ae.getActionCommand()); } public static void main(String[] args) { // Khai báo và khởi tạo frame CalculatorDemo myFrame = new CalculatorDemo(); 181
  48. myFrame.setVisible(true); // Hiển thị frame } } Chương trình 6.22 sẽ cho kết quả như hình 6.16: Hình 6.16: Kết quả demo case study TỔNG KẾT CHƯƠNG 6 Nội dung chương 6 đã trình bày phương pháp lập trình giao diện với các đối tượng trong thư viện chuẩn AWT của Java: Các đối tượng có chức năng làm vật chứa cho các đối tượng giao diện: Frame, Panel, Dialog. Các đối tượng là thành phần giao diện: Label, Button, TextField, TextArea, Checkbox, List, Menu. PTIT Phương pháp nắm bắt và xử lí các sự kiện đặc thù của từng đối tượng giao diện. Các phương pháp trình bày trên các vật chứa: FlowLayout, GridLayout, BorderLayout, GridBagLayout, NullLayout. Chương 6 cũng giới thiệu một phương pháp lập trình giao diện Java được nhúng trong các trang web, đó là lập trình applet: Cách tạo ra một applet với các phương thức cơ bản. Cách nhúng một applet vào một trang web. Cách kiểm nghiệm một applet sau khi nhúng vào trang web bằng các trình duyệt. Ngoài ra, chương này cũng giới thiệu cách sử dụng thư viện các đối tượng đồ hoạ mở rộng JFC của Java. Các đối tượng của thư viện JFC có chức năng hoàn toàn tương tự các đối tượng tương ứng trong thư viện chuẩn AWT. Ngoài ra, chúng còn được bổ sung thêm một số khả năng đồ hoạ cao cấp. 182
  49. CÂU HỎI VÀ BÀI TẬP CHƯƠNG 6 1. Viết chương trình thay đổi màu nền của frame theo lựa chọn của người dùng: Tạo ra các nút nhấn có tên theo các màu: Blue, Cyan, Gray, Green, Magenta, Orange, Pink, Red, White, Yellow. Khi click chuột vào nút nào, màu nền của frame sẽ đổi theo màu đó. 2. Viết chương trình thay đổi màu nền trong bài 1 bằng ô văn bản. Tạo ra một ô văn bản duy nhất, khi người dùng gõ vào một trong số các màu trong bài 1 và gõ enter, màu nền của frame sẽ đổi theo màu đó. Nếu người dùng gõ sai màu, không làm gì cả. 3. Viết chương trình thay đổi màu nền trong bài 1 bằng nút chọn radio. Tạo một nhóm các nút radio tương ứng với các loại màu. Khi màu nào được chọn, màu nền của frame sẽ thay đổi theo màu đó. 4. Viết chương trình thay đổi màu nền trong bài 1 bằng danh sách chọn list. Tạo một List có các item tương ứng với các loại màu. Khi màu nào được chọn, màu nền của frame sẽ thay đổi theo màu đó. 5. Viết chương trình thay đổi màu nền trong bài 1 bằng menu. Tạo một menubar, trên đó có gắn một menu tên là color, khi click chuột vào mune color, sẽ sổ xuống các màu tương ứng trong bài 1. Khi màu nào được chọn, màu nền của frame sẽ thay đổi theo màu đó. 6. Viết chương trình thay đổi màu nền trong bài 1 bằng menu popup. Tạo một menu popup trong frame, khi click chuột phải lên frame, sẽ hiện ra menu gồm các màu tương ứng trong bài 1. Khi màu nào được chọn, màu nền của frame sẽ thay đổi theo màu đó. PTIT 7. Viết lại các chương trình trong các bài tập 1 đến 6 dưới dạng applet và nhúng chúng vào trang myHtml.htl để chạy. (Trang này phải cùng thư mục với các lớp vừa cài đặt và biên dịch). 183
  50. CHƯƠNG 7 THƯ VIỆN CÁC COLLECTION TRONG JAVA VÀ ÁP DỤNG. Chương này giới thiệu thư viện Collections, các thành phần, cách sử dụng. Đây là nội dung quan trọng để sinh viên nâng cao kỹ năng lập trình hướng đối tượng với Java PTIT 184
  51. PTIT 7.1 Các thành phần của Collection Hình vẽ dưới đây mô tả cấu trúc chung của các thành phần trong thư viện collection nói chung. Trong đó: Phía trên là các interface mô tả các kiểu cơ bản và được hiện thực hóa trong các lớp ở mức dưới. Các lớp trong cấu trúc cây sẽ có tập hành động tương tự nhau dựa trên việc cài đặt các hàm thống nhất (giới thiệu trong phần sau). 185
  52. Hình 7.1: Cấu trúc các lớp trong thư viện Collection PTIT 186
  53. 7.2 Giới thiệu Collection: Định nghĩa: Một Collection đại diện cho 1 nhóm các đối tượng và được xác định như các phần tử của nó. Collection interface được dùng để truyền tập các đối tượng mà yêu cầu tổng quát được đặt lên hàng đầu. Ví dụ, thông thường tất cả những cài đặt collection chung đều có 1 hàm khởi tạo sử dụng tham số kiểu Collection. Hàm này còn được gọi là hàm khởi tạo chuyển đổi (conversion constructor), giúp khởi tạo collection mới để có thể chứa được tất cả các phần tử có trong một Collection nào đó. Hay hiểu theo một cách khác, nó cho phép bạn chuyển kiểu Collection. Giả sử, cho ví dụ, bạn có 1 Collection c, có thể là List, Set hoặc 1 dạng nào đó của Collection. Khai báo này có thể tạo ra 1 ArrayList mới (1 cài đặt của interface List). Khai báo: List list = new ArrayList (c); Ví dụ về 1 số phương thức của Collection: public interface Collection extends Iterable { // Thao tác cơ bản int size(); boolean isEmpty(); boolean contains(Object element);PTIT // Tùy chọn boolean add(E element); // Tùy chọn boolean remove(Object element); Iterator iterator(); // Phép toán số lượng lớn (Bulk operations) boolean containsAll(Collection c); // Tùy chọn boolean addAll(Collection c); // Tùy chọn 187
  54. boolean removeAll(Collection c); // Tùy chọn boolean retainAll(Collection c); // Tùy chọn void clear(); // Thao tác trên mảng Object[] toArray(); T[] toArray(T[] a); } Qua những ví dụ trên, ta thấy được rằng Collection đã đại diện cho 1 nhóm các đối tượng như thế nào. Nó chứa những phương thức cho biết có bao nhiêu phần tử trong Collection (isEmpty, size), kiểm tra xem 1 phần tử nào đó có ở trong Collection hay không (contains), thêm, xóa phần tử (add, remove) và cung cấp vòng lặp trong toàn bộ Collection (iterator). Phương thức add được định nghĩa một cách khá tổng quát. Phương thức sẽ bảo đảm rằng Collection sẽ chứa phần tử thêm vào sau khi lời gọi hoàn tất, và trả về true nếu Collection thay đổi theo lời gọi. Tương tự, phương thức remove được thiết kế để xóa một thể hiện của phần tử nhất định khỏi Collection, giả định rằng Collection có chứa phần tử, thì phương thức trả về true nếu Collection thay đổi theo lời gọi. Duyệt Collection: PTIT - Có 2 cách duyệt Collection: (1) dùng cấu trúc for- each; (2) sử dụng Iterators Sử dụng for – each: Cấu trúc for-each cho phép duyệt Collection hoặc Mảng sử dụng vòng for. Ví dụ sau in ra mỗi phần tử trên 1 dòng. VD: for (Object o : collection) System.out.println(o); Sử dụng Iterator: Iterator là 1 đối tượng cho phép duyệt Collection, xóa phần tử trong Collection 1 cách có chọn lựa. Tạo 1 đối tượng Iterator bằng cách gọi phương thức iterator. Dưới đây là interface Iterator: public interface Iterator { boolean hasNext(); 188
  55. E next(); void remove(); //optional } Phương thức hasNext trả về true nếu còn phần tử, và phương thức next trả về phần tử tiếp theo của vòng lặp. Phương thức remove xóa phần tử được trả về bởi next. Mỗi lần gọi hàm next chỉ được gọi 1 lần hàm remove, và exception sẽ sinh ra nếu quy tắc này không được tuân thủ. Chú ý rằng Iterator.remove là cách an toàn DUY NHẤT để sửa Collection trong quá trình duyệt. Các hành vi không xác định có thể sinh ra nếu dùng cách khác để duyệt và sửa( ví dụ dùng cấu trúc for -each) Sử dụng Iterator thay vì cấu trúc for – each trong các trường hợp: Xóa phần tử hiện tại. Cấu trúc for- each ẩn vòng lặp đi, vì thế không thể gọi hàm remove. Do đó, cấu trúc for-each không sử dụng để lọc được. Lặp song song qua nhiều Collection Phương thức sau đây chỉ ra cách sử dụng 1 Iterator để lọc 1 Collection tùy ý (duyệt qua Collection và xóa phần tử chỉ định) static void filter(Collection c) { for (Iterator it = c.iterator(); it.hasNext(); ) if (!cond(it.next())) it.remove(); } PTIT Đoạn code trên mang tính đa hình, nó có thể thực hiện trên bất cứ Collection nào mà không quan tâm đến cách cài đặt. Ví dụ trên đã minh họa sự dễ dàng khi viết thuật toán đa hình sử dụng Java Collection Framework. Thao tác số lượng lớn (Bulk) Thao tác Bulk thực hiện 1 thao tác trên toàn bộ Collection. Có thể cài đặt những thao tác Bulk bằng những thao tác cơ bản, mặc dù trong phần lớn trường hợp, những cài đặt này tỏ ra kém hiệu quả hơn. Dưới đây là những thao tác Bulk: containsAll – trả về true nếu Collection đích chứa toàn bộ các phần tử có trong Collection chỉ định. addAll – thêm tất cả phần tử trong Collection chỉ định vào Collection đích. removeAll – xóa tất cả những phần tử trong Collection đích mà CÓ TRONG Collection chỉ định. 189
  56. retainAll - xóa tất cả những phần tử trong Collection đích mà KHÔNG CÓ TRONG Collection chỉ định. Nghĩa là, nó chỉ giữ lại những phần tử có ở trong Collection chỉ định. clear – xóa toàn bộ phần tử trong Collection. Giải thích: - Từ các định nghĩa trên, ta có thể mô tả được một số phương thức dựa trên lý thuyết tập hợp như sau: o containsAll: Nhìn chung phương thức mô phỏng khá đúng mối quan hệ tập con – cha: từ hình trên ta thấy : Mọi phần tử của A phải nằm trong B hay B.containsAll(A) == true; o addAll: Là mô phỏng của phép hợp, ta cũng có thể biểu diễn dưới dạng giản đồ Venn: PTIT o removeAll: Dễ dàng nhận thấy đây là phép trừ 2 tập hợp: xóa những phần tử trong Collection hiện tại mà có trong Collection tham số. Ta chỉ lấy phần còn lại của Collection hiện tại 190
  57. o retainAll: Phương thức này là 1 mô phỏng của phép giao: Xóa phần tử trong A mà không có trong B. Vậy phần còn lại của tập A chính là giao của 2 tập. Phương thức addAll, removeAll, retainAll trả về true nếu Collection đích được thay đổi trong quá trình thực hiện lờiPTIT gọi thao tác. Minh họa về khả năng của thao tác Bulk, ta lấy ví dụ về xóa tất cả các thể hiện của 1 phần tử chỉ định nào đó, phần tử e, khỏi Collection c. c.removeAll(Collections.singleton(e)); Cụ thể hơn, giả sử ta muốn xóa toàn bộ những phần tử null khỏi Collection. c.removeAll(Collections.singleton(null)); Câu lệnh sử dụng Collections.singleton, 1 phương thức tĩnh, trả về 1 Set bất biến chỉ chứa những phần tử xác định (khác null). Thao tác mảng (array): 191
  58. Phương thức toArray được sử dụng như 1 cầu nối giữa Collection và các API đời cũ (giao diện lập trình ứng dụng), cái muốn đầu vào (input) là mảng. Thao tác mảng cho phép nội dung của Collection có thể được chuyển thành 1 mảng. Ví dụ, giả sử c là 1 Collection. Đoạn code dưới đây đưa ra nội dung của c được biểu diễn bằng mảng Object, có độ dài mảng bằng với số phần tử trong c. Object[] a = c.toArray(); Giả sử c chỉ chứa xâu (có thể c có kiểu Collection ). Đoạn code dưới đây đưa ra nội dung của c được biểu diễn bằng mảng String, độ dài mảng chính là số xâu có trong c. String[] a = c.toArray(new String[0]); PTIT 192
  59. 7.3 Giới thiệu List và cách sử dụng Định nghĩa: List là một Collection có thứ tự (hay còn gọi là 1 chuỗi) . List có thể chứa các thành phần giống nhau. Ngoài chức năng thừa hưởng từ Colection , List interface bao gồm: Positional access (Truy cập vị trí) – quản lý các phần tử dựa trên vị trí của chúng trong List Search (Tìm kiếm) - tìm kiếm một đối tượng quy định trong List và trả về số vị trí của nó. Iteration (Lặp đi lặp lại)PTIT – vận dụng tính chất tuần tự của danh sách Range-view - thực hiện các hoạt động phạm vi tùy ý trong danh sách. Ví dụ List interface: public interface List extends Collection { // Positional access E get(int index); // optional E set(int index, E element); // optional boolean add(E element); // optional void add(int index, E element); // optional 193
  60. E remove(int index); // optional boolean addAll(int index, Collection c); // Search int indexOf(Object o); int lastIndexOf(Object o); // Iteration ListIterator listIterator(); ListIterator listIterator(int index); // Range-view List subList(int from, int to); } Trong Java, INTERFACE LIST, bao gồm 3 phần là ARRAY LIST, VECTOR VÀ LINKED LIST. ArrayList giúp việc thực hiện hoạt động tốt hơn, và LinkedList cung cấp hiệu suất tốt hơn trong những tình huống nhất định. Ngoài ra, Vector đã được trang bị thêm để thực hiện List . So sánh với Vector Nếu bạn đã sử dụng Vector ,PTIT bạn đã quen với những điều cơ bản của List . (Tất nhiên, List là một interface, trong khi Vector là một “thực thi” cụ thể) .List sửa một số thiếu sót API nhỏ trong Vector . Việc sử dụng Vector, chẳng hạn như phương thức elementAt và setElementAt , giúp ta đặt tên rõ ràng hơn. Ta xem ví dụ dưới đây: a[i] = a[j].times(a[k]); Vector tương đương: v.setElementAt(v.elementAt(j).times(v.elementAt(k)), i); List tương đương là: v.set(i, v.get(j).times(v.get(k))); Bạn có thể đã nhận thấy rằng phương thức set đã thay thế phương thức trong Vector là setElementAt , đảo ngược thứ tự của các đối số để chúng phù hợp với hoạt động mảng tương ứng. Xem xét ví dụ sau: gift[5] = "golden rings"; 194
  61. Vector tương đương là: gift.setElementAt("golden rings", 5); List tương đương là: gift.set(5, "golden rings"); Vì lợi ích của tính nhất quán của, phương thức add (int, E) , thay thế insertElementAt (Object, int) , cũng đảo ngược thứ tự của các đối số. Ba thao tác phạm vi trong Vector ( indexOf , LastIndexOf , và setSize ) đã được thay thế bởi Range-view (subList) mạnh mẽ và phù hợp hơn rất nhiều. Collection Operations Thực thi remove luôn luôn loại bỏ sự xuất hiện đầu tiên của các phần tử quy định khỏi List . Thực thi add và addAll luôn luôn nối thêm các phần tử mới ở cuối list. Như vậy, móc nối cấu trúc sau đây từ List này với List khác. list1.addAll (List2); Đây là một hình thức không phá hủy cấu trúc, khi viết List thứ ba bao gồm List thứ hai nối vào đầu tiên. List list3 = new ArrayList (list1); list3.addAll(list2); Như set interface, List bổ sung các yêu cầu bằng phương thức equals và hashCode để hai List đối tượng có thể được so sánh mà không quan tâm đến các lớp thực hiện. Hai List đối tượng bằng nhau nếu chúng có chứa các phần tử tương đồng và giống nhauPTIT về thứ tự. Truy cập vị trí và tim kiếm trong List: Các hoạt động truy cập vị trí cơ bản (get, set, add and remove) giống như Vector ( elementAt , setElementAt , insertElementAt và removeElementAt ) có một ngoại lệ đáng chú ý. Các thiết lập (set ) và loại bỏ(remove) các hoạt động trả về giá trị cũ đang được ghi đè hoặc loại bỏ; các Vector counterparts ( setElementAt và removeElementAt ) không trả về giá trị gì ( void ). Các hoạt động tìm kiếm indexOf và LastIndexOf thực hiện chính xác như các hoạt động trong Vector . addAll thực thi chèn tất cả các phần tử của Collection xác định bắt đầu từ vị trí được xác định trước. Các phần tử được đưa vào theo thứ tự chúng được trả về bởi Collection‘s iterator. Xác định. Lời gọi này tương tự lời gọi addAll trong Collection. 195
  62. Đây là một đoạn code để hoán đổi hai giá trị được lập trong một Collection . public static void swap(List a, int i, int j) { E tmp = a.get(i); a.set(i, a.get(j)); a.set(j, tmp); } Tất nhiên có một sự khác biệt lớn. Đây là một thuật toán đa hình: nó hoán đổi hai phần tửtrong bất kỳ List , bất kể loại phần tử nào. Đây là thuật toán trao đổi có sử dụng phương thức swap: public static void shuffle(List list, Random rnd) { for (int i = list.size(); i > 1; i ) swap(list, i - 1, rnd.nextInt(i)); } Nó chạy từ cuối danh sách lên, nhiều lần hoán đổi một phần tử bất kỳ vào vị trí hiện tại. Tất cả các hoán vị xảy ra với khả năng như nhau. Các chương trình ngắn sau đây sử dụng thuật toán này để in các từ trong danh sách đối số của nó trong thứ tự ngẫu nhiên: PTIT import java.util.*; public class Shuffle { public static void main(String[] args) { List list = new ArrayList (); for (String a : args) list.add(a); Collections.shuffle(list, new Random()); System.out.println(list); } } 196
  63. Vòng lặp (Iterators) List cũng cung cấp một cách lặp phong phú hơn, được gọi là ListIterator , cho phép bạn đi qua các List theo hai hướng, sửa đổi List trong quá trình lặp, lấy được vị trí hiện tại của vòng lặp. Ví dụ: public interface ListIterator extends Iterator { boolean hasNext(); E next(); boolean hasPrevious(); E previous(); int nextIndex(); int previousIndex(); void remove(); //optional void set(E e); //optional void add(E e); //optional } Đây là câu lệnh tiêu chuẩn cho phép lặp cũ thông qua một list. for (ListIterator it = list.listIterator(list.size()); it.hasPrevious(); ) { Type t = it.previous(); } PTIT 5 vị trí con trỏ Việc gọi đến next và previous có thể được trộn lẫn, nhưng bạn phải cẩn thận một chút.Lần gọi đầu tiên phương thức previous trả về cùng một phần tử như gọi lần cuối đến next . Tương tự, khi gọi next lần đầu cũng trả về cùng một phần tử như lần gọi previous cuối. Sẽ không có gì phải bất ngờ khi phương thức nextIndex lại trả về chỉ số của phần tử, mà phần tử này sẽ được trả về bởi lời gọi hàm next sau đó. Tương tự như vậy với phương thức previousIndex và previous . Lời gọi thường được sử dụng hoặc để lấy ra vị trí tìm thấy phần tử nào đó hoặc để ghi lại vị trí của ListIterator để một ListIterator khác với vị trí (bắt đầu lặp) giống hệt có thể được tạo ra. 197
  64. Cũng sẽ không có gì bất ngờ khi giá trị trả về bởi nextIndex luôn luôn lớn hơn 1 đơn vị với giá trị được trả về bởi previousIndex . Nếu gọi previousIndex khi con trỏ ở trước vị trí đầu tiên, giá trị -1 sẽ trả về, và giá trị list.size() sẽ được trả về khi gọi nextIndex mà con trỏ ở sau vị trí cuối cùng . Để việc cài đặt được chặt chẽ, ta nên sử dụng phương thức List.indexOf public int indexOf(E e) { for (ListIterator it = listIterator(); it.hasNext(); ) if (e == null ? it.next() == null : e.equals(it.next())) return it.previousIndex(); // Element not found return -1; } Lưu ý rằng indexOf trả về giá trị it.previousIndex () mặc dù nó duyệt phần tử trong List theo hướng đầu danh sách trở đi. Lý do là it.nextIndex () sẽ trả về chỉ số của các phần tử mà chúng ta sẽ kiểm tra, và chúng ta muốn trả lại chỉ số của phần tử chúng ta đã kiểm tra public static void replace(List list, E val, E newVal) { for (ListIterator it = list.listIterator(); it.hasNext(); ) if (val == null ? it.next() == null : val.equals(it.next())) it.set(newVal); PTIT } Phương thức Add chèn một phần tử mới vào List ngay lập tức trước khi con trỏ trỏ vào vị trí hiện tại. Phương thức này được minh họa trong các thuật toán đa hình sau đây để thay thế tất cả các lần xuất hiện của một giá trị được chỉ định với các chuỗi giá trị có trong list quy định. public static void replace(List list, E val, List newVals) { for (ListIterator it = list.listIterator(); it.hasNext(); ){ if (val == null ? it.next() == null : val.equals(it.next())) { it.remove(); for (E e : newVals) it.add(e); } 198
  65. } } Range – view: Cách range-view hoạt động, subList(int fromIndex, int toIndex) , trả về một List của các phần của List này có chỉ số nằm trong khoảng từ fromIndex , đến toIndex . half-open range là ví dụ điển hình vòng lặp for. for (int i = fromIndex; i List dealHand(List deck, int n) { int deckSize = deck.size(); List handView = deck.subList(deckSizePTIT - n, deckSize); List hand = new ArrayList (handView); handView.clear(); return hand; } Danh sách phương thức: sort — Sắp xếp 1 list sử dụng thuật toán hợp nhất shuffle — Hoán chuyển ngẫu nhiên các thành phần trong list reverse - đảo ngược thứ tự của các thành phần trong một List . rotate — xoay tất cả các thành phần trong một List một bởi khoảng cách quy định. swap —hoán đổi thành phần tại các vị trí quy định trong một List . 199
  66. replaceAll — thay thế tất cả các lần xuất hiện của một giá trị nhất định với nhau fill — ghi đè lên tất cả các thành phần trong một List với giá trị quy định copy — Sao chép list binarySearch — tìm kiếm một phần tử trong một lệnh List bằng cách sử dụng thuật toán tìm kiếm nhị phân indexOfSubList — trả về chỉ số của List phụ đầu tiên lastIndexOfSubList trả về chỉ số của List phụ cuối cùng PTIT 200
  67. ArrayList: Định nghĩa: ArrayList là một phiên bản thông minh hơn của mảng. Thuộc không gian tên System.Collection.ArrayList, lớp ArrayList có những đặc điểm của Collection (tập hợp) hơn là mảng như : - Kích thước mảng cố định theo khai báo còn ArrayList có thể tự động giãn theo yêu cầu. - Nếu mảng cần định kích thước, gán trị thì ArrayList cung cấp các phương thức cho phép thêm, chèn, xóa một phần tử trong tập hợp. - Các phần tử của mảng phải cùng một kiểu dữ liệu, còn các phần tử của ArrayList có kiểu chung làObject, nghĩa là có thể có các kiểu khác. Cách truy xuất đến một phần tử của ArrayList cũng như cách truy xuất phần tử của mảng. Danh sách liên kết (Linked List) Định nghĩa: Danh sách liên kết (linked list) là một cấu trúc dữ liệu bao gồm một nhóm các nút (nodes) tao thành một chuỗi. Thông thường mỗi nút gồm dữ liệu (data) ở nút đó và tham chiếu (reference) đến nút kế tiếp trong chuỗi. Danh sách liên kết là một trong những cấu trúc dữ liệu đơn giản và phổ biến nhất. PTIT Ưu điểm: Cung cấp giải pháp để chứa cấu trúc dữ liệu tuyến tính. Dễ dàng thêm hoặc xóa các phần tử trong danh sách mà không cần phải cấp phát hoặc tổ chức lại trật tự của mảng. Cấp phát bộ nhớ động Nhược điểm: Một danh sách liên kết đơn giản không cho phép truy cập ngẫu nhiên dữ liệu. Chính vì lí do trên mà một số phép tính như tìm phần tử cuối cùng, xóa phần tử ngẫu nhiên hay chèn thêm, tìm kiếm có thể phải duyệt tất cả các phần tử. Phân loại: 201
  68. Danh sách tuyến tính (Linear list): Danh sách vòng (circular list): Danh sách liên kết đôi (Double list): Cấu trúc: Data: Thành phần chứa một hay nhiều biến dữ liệu. Next ptr: Tham chiếu trỏ đến phần tử kế tiếp trong cấu trúc. Head: biến tham chiếu trỏ đến phần tử đầu tiên của danh sách. PTIT Ví dụ khai báo: Struct LLnode { DataType Data; LLnode* next; }; Các phép toán: Khai báo: 202
  69. struct LLintNode { int Data; struct LLintNode* Next; }; Đếm số phần tử của Linked List: Duyệt từng phần tử rồi đếm, cho đến khi nào gặp phần tử cuối. int LengthLL(LLNode* head) { int length = 0; while (head != NULL) { ++length; head = head ->Next; } return length; } Thêm một phần tử vào cuối linked list: Nếu danh sách rỗng, thêm nút vào head. Ngược lại, tìm phần tử cuối cùng của danh sách rồi thêm nút mới vào Next của nút cuối cùng đó: PTIT void AddLast(LLNode head, int data) { LLNode tmp = head; LLNode* NewNode; NewNode = (LLNode*) malloc(sizeof(LLNode)); NewNode->Data = data; NewNode->Next = NULL; if ((*tmp) == NULL) { (*tmp) = NewNode; } else { while ((*tmp)->Next !=NULL) { 203
  70. tmp = &((*tmp)->Next); } (*tmp)->Next = NewNode; } } Ngoài những mô tả các thao tác kể trên, dựa vào cấu trúc trên ta hoàn toàn có thể tạo ra phương thức addLast, removeFirst, insertAt VECTOR: Định nghĩa: Vector cũng tương tự như ArrayList nhưng dùng cơ chế đồng bộ . Như vậy, nó là kiểu dữ liệu lý tưởng khi thao tác với các ứng dụng đa luồng. Các method trong bài này nằmPTIT ở 2 class java.util.Vector và java.util.Enumeration Khai báo: Vector vt = new Vector(); Nhập dữ liệu cho một Vector (class Console nằm trong gói corejava) Lưu ý là mỗi phần tử của Vector đều phải là một đối tượng, nên ta phải có new Integer(n) khi muốn đưa vào một biến kiểu int. Tương tự với Byte, Long, Float, do { int n = Console.readInt(""); if(n!=0) vt.addElement(new Integer(n)); 204
  71. } while(n!=0); In ra các phần tử của một Vector for(int i=0;i<vt.size();i++) System.out.println(vt.elementAt(i)); Để đưa Vector về kiểu mảng cho dễ thao tác, ta đưa về kiểu Enumeration (một kiểu mảng) Enumeration e = vt.elements(); Như vậy ta có mảng e kiểu Enumeration sao chép y khuôn Vector vt để dễ xử lí, không đụng đến Vector vt In ra các phần tử của một Enumeration while(e.hasMoreElements()) System.out.println(e.nextElement()); Vector và Arraylist chúng có tác dụng gần giống nhau nhưng thực tế Vector có cơ chế đồng bộ hóa vì thế chúngPTIT ta có thể cập nhật chúng tại nhiều luồn khác nhau.Còn ArrayList chúng ta không thể làm như vậy chúng sẽ ném ra cho ta một ngoại lệ nếu chúng ta cố tình cập nhật nó cùng một lúc tại nhiều luồng 205
  72. 3/ Giới thiệu Queue: Định nghĩa: Một Queue là một Collection để lưu trữ các phần tử trước khi cài đặtviêc truy cập. Bên cạnh các hoạt động Collection cơ bản , queue cung cấp việc chèn thêm , loại bỏ , và các hoạt động kiểm tra . Interface queueđược cho sau đây: public interface Queue extends Collection { E element(); boolean offer(E e); E peek(); E poll(); E remove(); } Mỗi phương thức Queue tồn tại dưới hai hình thức: ( 1 ) một ném một Exception (ngoại lệ) nếu hoạt động bị lỗi , và (2) ngoài ra là trả về một giá trị đặc biệt nếu hoạt động bị lỗi (hoặc null hoặc sai , tùy thuộc vào hoạt động ) . Cấu trúc thông thường của interface được minh họa trong bảng dưới đây . CẤU TRÚC INTERFACE HÀNG ĐỢI Loại hoạt động NémPTIT trả lại ngoại lệ Trả lại giá trị đặc biệt Insert add(e) Offer(e) Remove remove() Poll() Examine Element() Peek() Queue thông thường, nhưng không nhất thiết , các phần tử được truy xuất theo thứ tự First In First Out (FIFO) Trong số các Exception là queue ưu tiên , thứ tự phần tử tùy theo giá trị của nó . Bất cứ việc sắp thứ tự nào được sử dụng , phần tử đứng đầu của queuelà các phần tử sẽ được gỡ bỏ bởi một lời gọi tới phương thức remove hoặc poll. Trong một queue FIFO , tất cả các phần tử mới được chèn vào cuối hàng đợi. Các loại queuecó thể sử dụng quy tắc vị trí khác nhau . Mỗi queuecài đặtphải xác định tính chất thứ tự của nó . 206
  73. Có thể cài đặt để giới hạn số lượng phần tử trong queue ; queue như vậy được gọi là bị chặn. Một vài cài đặt Queue trong java.util.concurrent bị chặn, nhưng điều ngược lại với java.util. - Phương thức add, cái mà queue thừa kế từ Collection ,là chèn một phần tử trừ khi nó sẽ vi phạm các hạn chế khả năng của queue, trong trường hợp nó ném ExceptionIllegalStateException . - Phương thức offer, được sử dụng trên Queue bị chặn, khác với add duy nhất ở chỗ trả về false nếu không thể thêm được phần tử vào. - Các phương thức remove và poll, cả hai đều xóa và trả lại (giá trị ) đầu queue. Các phương remove và poll thể hiện sự khác nhau chỉ khi queue rỗng . Trong hoàn cảnh này, phương thức remove ném ra NoSuchElementException , trong khi poll trả về null. - Các phương thức peek và element trả về , nhưng không xóa phần tử đứng đầu của hàng đợi. Chúng khác nhau ở điểm : Nếu queue rỗng , phương thức element ném ra Exception NoSuchElementException , trong khi peek trả về null. Queue thường không cho phépPTIT chèn các phần tử null. LinkedList được cài đặt từ Queue, là một ngoại lệ. Vì những lý do lịch sử , nó cho phép các phần tử null, nhưng bạn nên cẩn thận điều này , bởi vì null có thể là một giá trị trả lại đặc biệt khi sử dụng các phương thức poll và peek . Interface Queue không định nghĩa các phương thức chặn queue, cái khá phổ biến trong lập trình song song . Những phương thứcnày , được định nghĩa trong gói interface java.util.concurrent.BlockingQueue . Trong chương trình ví dụ sau đây , một queue được sử dụng để cài đặtmột đồng hồ đếm ngược . Queue được tải trước vào với tất cả các giá trị số nguyên từ một số quy định đến 0, theo thứ tự giảm dần . Sau đó , các giá trị được lấy ra khỏi queue và in trong khoảng thời gian một giây : 207
  74. import java.util.*; public class Countdown { public static void main(String[] args) throws InterruptedException { int time = Integer.parseInt(args[0]); Queue queue = new LinkedList (); for (int i = time; i >= 0; i ) queue.add(i); while (!queue.isEmpty()) { System.out.println(queue.remove()); Thread.sleep(1000); } } } Trong ví dụ sau đây , một Queue ưu tiên (priority Queue) được sử dụng để SẮP XẾP một tập hợp các phần tử . Mục đích của đoạn chương trình là để kiểm tra sự sắp xếp của Queue ưu tiên: Ví dụ: PTIT static List heapSort(Collection c) { Queue queue = new PriorityQueue (c); List result = new ArrayList (); while (!queue.isEmpty()) result.add(queue.remove()); return result; } Interface Deque 208
  75. Định nghĩa: Một deque là một double - ended – queue (hàng đợi 2 đầu). Một double- ended-queue là một tập hợp tuyến tính của các phần tử hỗ trợ chèn và loại bỏ các phần tử ở cả hai điểm đầu cuối. Interface Deque là một kiểu dữ liệu trừu tượng phong phú hơn cả Stack và Queue bởi vì nó cài đặt cả ngăn xếp và queue. Interface Deque , xác định phương thức để truy cập vào các phần tử ở cả hai đầu. Phương thức được cung cấp để chèn , xóa , và kiểm tra các phần tử. Các lớp tiền định nghĩa như ArrayDeque và LinkedList đều cài đặt interface Deque . Lưu ý rằng interface Deque có thể được sử dụng như last-in- first-out Stack (LIFO) và first-in-fitst-out Queue (FIFO) . Các phương thức trong interface Deque được chia thành ba phần: Thao tác chèn Các phương thức addfirst và offerFirst chèn các phần tử vào đầu Deque . Các phương thứcaddLast và offerLast phần tử chèn vào cuối các ví dụ Deque . Khi Deque bị chặn, các phương thức ưa thích là offerFirst và offerLast vì addFirst hoặc addLast có thể không ném một Exception nếu Deque đầy . Thao tác xóa Các phương thức removeFirst và pollFirst loại bỏ các phần tử đầu ví dụ Deque . Các phương thức removeLast và pollLast loại bỏ các phần tử cuối cùng . Các phương thức pollFirst và pollLast trả lại null nếu Deque trống trong khi các phương thức removeFirst và removeLast ném một Exception trong trường hợp Deque trống . Thao tác lấy phần tử PTIT Các phương thức getFirst và peekFirst lấy phần tử đầu tiên của Deque. Những phương thức này không loại bỏ các giá trị từ Deque. Tương tự như vậy , các phương thức getLast và peekLast lấy phần tử cuối cùng nhưng không loại bỏ . Các phương thức getFirst và getLast ném một Exception trong trường hợp deque trống trong khi các phương thức peekFirst và peekLast trả lại NULL. - 12 phương thức bao gồm chèn , loại bỏ và lấy phần tử được tóm tắt trong bảng dưới đây : Type of First Element (Beginning of Last Element (End of Operation the Deque instance) the Deque instance) 209
  76. addFirst(e) addLast(e) Insert offerFirst(e) offerLast(e) removeFirst() removeLast() Remove pollFirst() pollLast() getFirst() getLast() Examine peekFirst() peekLast() Ngoài các phương thức cơ bản để chèn, xóa và kiểm tra một Deque, interface Deque cũng có một số phương thức được xác định trước. Một trong số đó là removeFirstOccurence , phương thức này loại bỏ xuất hiện đầu tiên của các phần tử tham số nếu nó tồn tại trong Deque. Nếu phần tử không tồn tại sau đó các trường hợp Deque vẫn không thay đổi . Một phương thứctương tự là removeLastOccurence ; phương thứcnày loại bỏ sự xuất hiện cuối cùng của phần tử quy đinh nếu nó tồn tại trong trong Deque instance. Kiểu trả về của các phương thức này là boolean, và chúng trả về true nếu các phần tử tồn tại trong Deque. PTIT 210
  77. 7.4 Giới thiệu Set Interface: - Định nghĩa: Set là một loại Collections không chứa phần tử trùng nhau. Set biểu diễn một cách trừu tượng khái niệm tập hợp trong toán học. Trong interface Set chỉ bao gồm các phương thức được thừa kế từ Collections và thêm vào giới hạn là không cho phép có phần tử trùng nhau. Set cũng có những phương thức là equals và hashCode , cho phép những thể hiện của Set có thể dễ dàng so sánh với nhau ngay cả trong trường hợp chúng thuộc những loại thực thi khác nhau. Hai thể hiện được gọi là bằng nhau nếu chúng chứa những phần tử như nhau. Dưới đây những phương thức nằm trong Set interface : public interface Set extendsPTIT Collection { // Toan tu co ban int size(); boolean isEmpty(); boolean contains(Object element); // Tùy chọn boolean add(E element); // Tùy chọn boolean remove(Object element); Iterator iterator(); // Toan tu so luong lon boolean containsAll(Collection c); 211
  78. // Tùy chọn boolean addAll(Collection c); // Tùy chọn boolean removeAll(Collection c); // Tùy chọn boolean retainAll(Collection c); // Tùy chọn void clear(); // Toan tu cua mang Object[] toArray(); T[] toArray(T[] a); } Nền tảng Java bao gồm 3 lớp thực thi chính của Set là HashSet, TreeSet, LinkedHashSet. - HashSet lưu trữ phần tử trong bảng băm (hash table). Đây là lớp thực thi cho tốc độ tốt nhất, tuy nhiên lại không đảm bảo thứ tự của phần tử. - TreeSet lưu trữ phần tử trong cây đỏ-đen (red – black tree), sắp xếp phần tử dựa trên giá trị của chúng, và do đó chậm hơn đáng kể so với HashSet. - LinkedHashSet cũng dùngPTIT bảng băm để lưu phần tử, ngoài ra còn sử dụng danh sách liên kết để sắp xếp phần tử theo thứ tự mà phần tử được chèn vào tập hợp (insertion-order). LinkedHashSet giúp người dùng loại bỏ những trật tự hỗn độn không đoán trước gây ra bởi HashSet và với chi phí chỉ cao hơn một chút. Dưới đây là một code đơn giản nhưng rất hay dùng với Set. Giả sử bạn có 1 đối tượng Collection và bạn muốn tạo một đối tượng Collection khác chứa những phần tử giống như đối tượng cũ nhưng loại bỏ những phần tử thừa Collection noDups = new HashSet (c); Đối tượng mới sẽ được loại bỏ những phần tử thừa vì nó thuộc loại Set, theo định nghĩa sẽ không được chứa phần tử trùng nhau. Còn nếu muốn bảo toàn thứ tự phần tử như trong đối tượng gốc và cũng loại bỏ những phần tử bị trùng thì ta thay đoạn code trên bằng đoạn sau Collection noDups = new LinkedHashSet (c); 212
  79. Bên cạnh đó cũng có 1 phương thức generic bao trùm những đoạn code trên, trả về đối tượng Set với cùng kiểu mà chúng ta truyền vào . Đó là removeDups: public static Set removeDups(Collection c) { return new LinkedHashSet (c); } Những toán tử cơ bản trong Set Interface (Basic Operations) - int size() : trả về số phần tử trong Set ( lực lượng của chúng ) - boolean isEmpty() : kiểm tra xem Set rỗng hay không , trả về true nếu Set rỗng - boolean add( E element ) : thêm phần tử vào trong Set nếu nó chưa có trong Set đó, trả về true nếu chưa có phần tử trùng trong Set và false trong trường hợp ngược lại . - boolean remove(Object element) : xóa một phần tử được chỉ định trong Set. Trả về true nếu phần tử đó tồn tại trong Set và false trong trường hợp ngược lại. - Iterator iterator() : trả về kiểu Iterator của Set Chương trình dưới đây nhập vào danh sách đối số của hàm main và in ra những từ bị trùng, những từ khác nhau: import java.util.*; PTIT public class FindDups { public static void main(String[] args) { Set s = new HashSet (); for (String a : args) if (!s.add(a)) System.out.println("Duplicate detected: " + a); System.out.println(s.size() + " distinct words: " + s); } } 213
  80. Input là : i came i saw i left Sau khi chạy chương trình trên ta được : Duplicate detected: i Duplicate detected: i 4 distinct words: [i, left, saw, came] Chú ý rằng code luôn luôn tham chiếu tới Collection bởi kiểu Interface (ở đây là Set) hơn là kiểu thực thi (ở đây là HashSet) . Đây là điều đặc biệt trong việc luyện tập Java bởi nó tạo cho bạn sự linh hoạt trong việc thay đổi kiểu thực thi. Khi đó bạn chỉ cần thay đổi hàm khởi tạo . Kiểu thực thi của Set ở ví dụ trên thuộc kiểu HashSet. Đó là kiểu không bảo toàn thứ tự của phần tử trong Set. Nếu bạn muốn chương trình in ra danh sách từ theo thứ tự Alphabet, đơn giản bạn chỉ cần thay đổi kiểu thực thi của Set từ HashSet chuyển thành TreeSet. Set s = new TreeSet (); PTIT Kết quả in ra sẽ là Duplicate detected: i Duplicate detected: i 4 distinct words: [came, i, left, saw] Những toán tử số lượng lớn trong Set Interface (Bulk Operations) Những toán tử Bulk đặc biệt thích hợp với Set. Khi được áp dụng nó sẽ thực hiện những phép toán của tập hợp trong toán học. Giả sử ta có 2 tập hợp s1 và s2 . Sau đây là những phương thức thực hiện các phép toán Bulk trên 2 tập hợp s1 và s2 : 214
  81. - s1.containAll(s2) : trả về giá trị true nếu s2 là tập con của s1 ( s2 là tập con của s1 nếu s1 chứa tất cả phần tử trong s2 ) - s1.addAll(s2) : trả về tập hợp là hợp của s1 và s2 ( hợp là tập hợp chứa tất cả phần tử của 2 tập hợp) - s1.retainAll(s2) : trả về tập hợp là giao của s1 và s2 ( giao là tập hợp chỉ chứa phần tử giống nhau giữa 2 tập hợp ) - s1.removeAll(s2) : trả về tập hợp bù của s2 với s1 ( bù là tập hợp chứa các phần tử có trong s1 nhưng không có trong s2 ) Để thực hiện những phép toán hợp, giao , lấy phần bù của 2 tập hợp mà không làm thay đổi 2 tập hợp, trước khi gọi các toán tử Bulk ta phải sao chép một tập hợp sang một tập mới . Đây là ví dụ : Set union = new HashSet (s1); union.addAll(s2); Set intersection = new HashSet (s1); intersection.retainAll(s2); Set difference = new HashSet (s1); difference.removeAll(s2); PTIT Bây giờ ta sẽ tạo ra một chương trình FindDups . Giả sử bạn muốn biết từ nào trong danh sách từ chỉ xuất hiện một lần và từ nào xuất hiện nhiều hơn một lần. Tuy nhiên bạn lại không muốn lặp lại việc in ra phần tử trùng như ví dụ trên. Do đó thuật toán của chúng ta là tạo ra 2 tập hợp, 1 tập hợp chứa danh sach từ và tập kia chỉ chứa những từ bị trùng. Những từ mà chỉ xuất hiện một lần là phần bù của 2 tập hợp . Đây là chương trình FindDups : import java.util.*; public class FindDups2 { public static void main(String[] args) { Set uniques = new HashSet (); 215
  82. Set dups = new HashSet (); for (String a : args) if (!uniques.add(a)) dups.add(a); // Destructive set-difference uniques.removeAll(dups); System.out.println("Unique words: " + uniques); System.out.println("Duplicate words: " + dups); } } Giải thích code: - Lệnh uniques.add(a) ngoài việc thêm phần tử vào tập hợp, nó còn trả về kết quả thực hiện. Phương thức trả về true nếu thêm được, false nếu ngược lại. - Sau khi tạo được 2 tập hợp (uniques và dups), ta lấy phần bù (uniques - dups), kết quả là cho ra những phần tử chỉ thuộc tập uniques chứ không thuộc dups (nói cách khác chỉ giữ lại những phần tử xuất hiện duy nhất 1 lần) PTIT - Dĩ nhiên những phần tử xuất hiện trên (hoặc bằng) 2 lần sẽ ở trong tập dups Khi chạy với danh sách đối số như ví dụ trước (i came i saw i left) kết quả sẽ là : Unique words: [left, saw, came] Duplicate words: [i] Một phép toán ít phổ biến hơn là phép lấy phần bù cân xứng ( symmetric set difference ) – là tập hợp bao gồm phần tử của cả 2 tập hợp nhưng không chứa những phần tử giống nhau giữa 2 tập hợp. Đoạn code dưới đây sẽ thể hiện phép toán đó (Minh họa hình vẽ bên dưới): 216
  83. Set symmetricDiff = new HashSet (s1); symmetricDiff.addAll(s2); Set tmp = new HashSet (s1); tmp.retainAll(s2); symmetricDiff.removeAll(tmp); Giải thích code: - Đầu tiên ta “copy” nội dung của HashSet s1 vào SymmetricDiff - Sau đó tạo ra SymmetricDiff = s1 ∪ s2 bằng phương thức addAll (đã đề cập kỹ trong phần Collection). - Tập tmp được tạo ra chính là s1 ∩ s2 - Bước cuối cùng là lấy phần bù: SymmetricDiff – tmp. Kết quả có thể minh họa như hình vẽ trên. PTIT Toán tử với mảng trong Set Inteface (Arrays Operations) Toán tử với mảng trong Set không khác gì với những kiểu Collection khác . Nó đã được đề cập ở phần Collection Inteface phía trên Các kiểu thực thi của Set: Các kiểu thực thi của Set bao gồm 2 loại là thực thi thông thường (general- purpose) và thực thi đặc biệt (special-purpose) Kiểu thực thi thông thường (genenal-purpose) 217
  84. Như đã giới thiệu ở phần trên, Set bao gồm 3 lớp thực thi thông thường đó là HashSet, TreeSet và LinkedHashSet. Một điều đáng ghi nhớ là kiểu HashSet là một tập hợp tính tuyến tính giữa tổng số phần tử đưa vào với số lượng ô chứa ( dung lượng ) . Do đó lựa chọn dung lượng chứa khởi đầu quá lớn sẽ gây lãng phí không gian và thời gian. Mặt khác, lựa chọn dung lượng quá thấp sẽ làm tốn thời gian trong việc sao chép cấu trúc dữ liệu mỗi khi nó buộc phải tăng dung lượng lên. Nếu bạn không chỉ định dung lượng ban đầu, dung lượng mặc định sẽ là 16. Trong quá khứ, có một vài lợi thế khi lựa chọn dung lượng khởi đầu là những số nguyên tố. Tuy nhiên hiện nay điều này không còn đúng nữa. Thay vào đó sẽ là số mũ của 2. Dòng code dưới đây xác định một kiểu HashSet với dung lượng ban đầu là 64 Set s = new HashSet (64); Kiểu thực thi đặc biệt (Special-purpose) Set bao gồm 2 kiểu thực thi đặc biệt là EnumSet và CopyOnWriteArraySet. EnumSet là kiểu thực thi hữu hiệu đối với những kiểu enum. Tất cả thành viên trong tập hợp enum phải cùng một kiểu enum. Tập hợp Enum hỗ trợ duyệt các phần tử trong giới hạn của nó . Ví dụ cho trước 1 enum là các ngày trong tuần, bạn có thể duyệt qua tất cả các ngày trong tuần như sau : PTIT for (Day d : EnumSet.range(Day.MONDAY, Day.FRIDAY)) System.out.println(d); CopyOnWriteArraySet là kiểu thực thi theo phương pháp mảng copy-on-write. Tất cả những toán tử làm thay đổi tập hợp như add,set,and remove được thực hiện bằng cách tạo 1 bản sao của mảng. Không giống như hầu hết các kiểu thực thi của Set phương thức add ,remove và contains đòi hỏi thời gian cân xứng với kích thước của tập hợp. Do đó kiểu thực thi này chỉ thích hợp với những tập hợp mà chỉ cần duyệt phần tử chứ không cần thay đổi phần tử. SortedSet Inteface 218