第三十二講 集合框架——Set介面
Set介面概述
Set集合不允許儲存重複元素,而且不保證元素是有序的(存入和取出的順序有可能一致[有序],也有可能不一致[無序])。通過檢視JDK文件,發現Set集合的功能和Collection的是一致的,所以Set集合取出的方法只要一個,那就是迭代器。
Set介面的常用子類
HashSet
查閱HashSet集合的API介紹,可發現:
此類實現Set介面,由雜湊表(實際上是一個HashMap例項)支援。它不保證set的迭代順序,特別是它不保證該順序恆久不變。此類允許使用null元素。
通過上面的這句話,我們可以總結出:
- HashSet集合採用雜湊表結構儲存資料,保證元素唯一性的方式依賴於hashCode()與equals()方法(後面會介紹到);
- HashSet集合不能保證元素的迭代順序與元素儲存順序相同。
雜湊表
雜湊表概述
上面提到了HashSet集合採用雜湊表結構儲存資料,那什麼是雜湊表呢? 雜湊表底層使用的也是陣列機制,陣列中也存放物件,而這些物件往陣列中存放時的位置比較特殊,當需要把這些物件給陣列存放時,會根據這些物件的特有資料結合相應的演算法,計算出這個物件在陣列中的位置,然後把這個物件存放在陣列中。而這樣的陣列就稱為雜湊陣列,即就是雜湊表。
雜湊表原理
當向雜湊表中存放元素時,需要根據元素的特有資料結合相應的演算法,這個演算法其實就是Object類中的hashCode方法。由於任何物件都是Object類的子類,所以任何物件都擁有這個方法。即就是在雜湊表中存放物件時,會呼叫物件的hashCode方法,算出物件在表中的存放位置,這裡需要注意,如果兩個物件hashCode方法算出來的結果一樣,這種現象稱為雜湊衝突,這時會呼叫物件的equals方法,比較這兩個物件是不是同一個物件,如果equals方法返回的是true,那麼就不會把第二個物件存放在雜湊表中,如果返回的是false,就會把這個物件通過地址連結法或拉鍊法存放在雜湊表中。
雜湊表結構儲存資料的原理用圖來表示:
總結
保證HashSet集合元素的唯一,其實就是根據物件的hashCode和equals方法來決定的。如果我們往集合中存放自定義的物件,想要保證其唯一,就必須複寫hashCode和equals方法建立屬於當前物件的比較方式。覆蓋hashCode()方法是為了根據元素自身的特點確定雜湊值,覆蓋equals()方法是為了解決雜湊值的衝突。
雜湊表儲存自定義物件
例,往HashSet中儲存學生物件(姓名,年齡)。同姓名,同年齡視為同一個人,不存。
分析:HashSet中存放自定義型別元素時,需要重寫物件中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的物件唯一。
建立自定義物件Student:
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
/*
* 覆蓋hashCode方法,根據物件自身的特點定義雜湊值。
*/
public int hashCode() {
final int NUMBER = 37;
return name.hashCode() + age * NUMBER; // 儘量減小雜湊衝突
}
/**
* 還需要定義物件自身判斷內容相同的依據,覆蓋equals()方法。
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
throw new ClassCastException("型別錯誤");
}
Student stu = (Student) obj;
return this.name.equals(stu.name) && this.age == stu.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
建立HashSet集合,儲存Student物件:
public class HashSetTest {
public static void main(String[] args) {
// 1,建立容器物件
Set set = new HashSet();
// 2,儲存學生物件
set.add(new Student("xiaoqiang", 20));
set.add(new Student("wangcai", 27));
set.add(new Student("xiaoming", 22));
set.add(new Student("xiaoqiang", 20));
set.add(new Student("daniu", 24));
set.add(new Student("wangcai", 27));
// 3,獲取所有學生
for (Iterator it = set.iterator(); it.hasNext();) {
Student stu = (Student) it.next();
System.out.println(stu.getName() + "::" + stu.getAge());
}
}
}
注意:對於判斷元素是否存在,以及刪除、新增等操作,依賴的方法也是元素的hashCode()和equals()方法。
LinkedHashSet
通過查閱LinkedHashSet的API介紹,我們可知道:
具有可預知迭代順序的Set介面的雜湊表和連結列表實現。此實現與HashSet的不同之外在於,後者維護著一個運行於所有條目的雙重連結列表。
可總結為:LinkedHashSet是一個特殊的Set集合,而且是有序的,底層是一個雙向連結串列+雜湊表。
public class LinkedHashSetDemo {
public static void main(String[] args) {
// 1,建立一個Set容器物件
Set set = new LinkedHashSet();
// 2,新增元素
set.add("abc");
set.add("heihei");
set.add("haha");
set.add("nba");
// 3,只能用迭代器取出
for (Iterator it = set.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
執行以上程式,可知LinkedHashSet是有序的。
TreeSet
TreeSet是執行緒不同步的,可以對Set集合中的元素進行排序,底層資料結構是二叉樹(也叫紅黑樹),保證元素唯一性的依據是:比較方法的返回值是0。更通俗一點說就是比較方法的返回值是否是0,只要是0,就是重複元素,不存。
TreeSet對集合中的元素進行排序的方式有兩種,如下:
TreeSet儲存自定義物件,使用TreeSet排序的第一種方式
例1,往TreeSet集合中儲存自定義物件學生。想按照學生的年齡進行排序。
先看TreeSet排序的第一種方式——我們自定義的Student類須實現Comparable介面(該介面強制讓Student類具備比較性),覆蓋compareTo方法。
int compareTo(T o)
:比較此物件與指定物件的順序。如果該物件小於、等於或大於指定物件,則分別返回負整數、零或正整數。
自定義的Student類的程式碼為:
public class Student implements Comparable {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
/*
* 覆蓋hashCode方法,根據物件自身的特點定義雜湊值。
*/
public int hashCode() {
final int NUMBER = 37;
return name.hashCode() + age * NUMBER; // 儘量減小雜湊衝突
}
/**
* 還需要定義物件自身判斷內容相同的依據,覆蓋equals()方法。
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
throw new ClassCastException("型別錯誤");
}
Student stu = (Student) obj;
return this.name.equals(stu.name) && this.age == stu.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
/**
* 學生就具備了比較功能。該功能是自然排序使用的方法。
* 自然排序就以年齡的升序排序為主。
*/
@Override
public int compareTo(Object o) {
Student stu = (Student)o;
// 驗證TreeSet集合的add()方法呼叫了compareTo()方法
System.out.println(this.name + ":" + this.age + "......" + stu.name + ":" + stu.age) ;
if (this.age > stu.age)
return 1;
if (this.age < stu.age)
return -1;
return 0;
}
}
接下來編寫一個測試類——TreeSetDemo.java,其程式碼為:
public class TreeSetDemo {
public static void main(String[] args) {
Set set = new TreeSet();
set.add(new Student("xiaoqiang", 20));
set.add(new Student("daniu", 24));
set.add(new Student("xiaoming", 22));
set.add(new Student("tudou", 18));
set.add(new Student("dahuang", 19));
// 3,只能用迭代器取出
for (Iterator it = set.iterator(); it.hasNext();) {
Student stu = (Student) it.next();
System.out.println(stu.getName() + "::" + stu.getAge());
}
}
}
執行以上程式,會發現TreeSet集合中儲存的學生真是按照年齡來升序排序的。
接著我們面臨的需求又發生了變化,同姓名同年齡的學生視為同一個人,是不用存入TreeSet集合中的,而且當年齡相同時,需要按照姓名的自然順序排序。這時自定義的Student類的程式碼需要修改為:
package cn.liayun.domain;
public class Student implements Comparable {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
/**
* 覆蓋hashCode方法,根據物件自身的特點定義雜湊值。
*
*/
public int hashCode() {
final int NUMBER = 37;
return name.hashCode() + age * NUMBER;
}
/**
* 需要定義物件自身判斷內容相同的依據,覆蓋equals方法。
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Student)) {
throw new ClassCastException(obj.getClass().getName() + "型別錯誤");
}
Student stu = (Student) obj;
return this.name.equals(stu.name) && this.age == stu.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
/**
* 學生就具備了比較功能。該功能是自然排序使用的方法。
* 自然排序就以年齡的升序排序為主。
*/
@Override
public int compareTo(Object o) {
Student stu = (Student) o;
// System.out.println(this.name + ":" + this.age + "......." + stu.name + ":" + stu.age);
/*
* 既然是同姓名同年齡是同一個人,視為重複元素,要判斷的要素有兩個。
* 既然是按照年齡進行排序,所以先判斷年齡,再判斷姓名。
*/
int temp = this.age - stu.age;
return temp == 0 ? this.name.compareTo(stu.name) : temp;
// return 1;
}
}
這時測試類——TreeSetDemo.java的程式碼應改為:
public class TreeSetDemo {
public static void main(String[] args) {
Set set = new TreeSet();
set.add(new Student("xiaoqiang", 20));
set.add(new Student("daniu", 24));
set.add(new Student("xiaoming", 22));
set.add(new Student("tudou", 18));
set.add(new Student("daming", 22));
set.add(new Student("dahuang", 19));
// 3,只能用迭代器取出
for (Iterator it = set.iterator(); it.hasNext();) {
Student stu = (Student) it.next();
System.out.println(stu.getName() + "::" + stu.getAge());
}
}
}
執行結果為:
圖解TreeSet儲存元素的自然排序和唯一性
思考一個這樣的問題:元素變為怎麼存進去的就怎麼取出來的,怎麼做呢?
這時可依據二叉樹原理來實現,只要讓compareTo()方法返回正數即可。
import java.util.*;
class Student implements Comparable { // 該介面強制讓學生具備比較性
private String name;
private int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int compareTo(Object obj) {
return 1;
}
}
class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Student("lisi02", 22));
ts.add(new Student("lisi007", 20));
ts.add(new Student("lisi09", 19));
ts.add(new Student("lisi08", 19));
Iterator it = ts.iterator();
while(it.hasNext()) {
Student stu = (Student)it.next();
System.out.println(stu.getName()+"..."+stu.getAge());
}
}
}
TreeSet儲存自定義物件,使用TreeSet排序的第二種方式
例,將Student物件儲存到TreeSet集合中,同姓名同年齡視為同一個人,不存,按照學生的姓名進行升序排序,而且當姓名相同時,需要按照學生的年齡進行升序排序。
分析:當元素自身不具備比較性時,或者具備的比較性不是所需要的,這時就需要讓容器自身具備比較性。定義一個比較器,將比較器物件作為引數傳遞給TreeSet集合的建構函式。當兩種排序都存在時,以比較器為主。
我們自定義的Student類的程式碼沒必要修改。接著,自定義一個比較器實現Comparator介面,覆蓋compare()方法。
package cn.liayun.comparator;
import java.util.Comparator;
import cn.liayun.domain.Student;
/**
* 自定義了一個比較器,用來對學生物件按照姓名進行升序排序。
* @author liayun
*
*/
public class ComparatorByName /*extends Object*/ implements Comparator
相關推薦
第三十二講 集合框架——Set介面
Set介面概述
Set集合不允許儲存重複元素,而且不保證元素是有序的(存入和取出的順序有可能一致[有序],也有可能不一致[無序])。通過檢視JDK文件,發現Set集合的功能和Collection的是一致的,所以Set集合取出的方法只要一個,那就是迭代器。
Set介面的常用子類
第三十一講 集合框架——List介面
List介面概述
Collection介面有兩個子介面:List(列表)、Set(集),本文我們先重點學習List(列表)介面。查閱API,檢視List的介紹,我們可以發現以下這些話語:
有序的collection(也稱為序列)。此介面的使用者可以對列表中每個元素的插入位
第三十六講 集合框架工具類
Collections工具類
Collections類概述
針對集合操作的工具類,裡面的方法都是靜態的,可以對集合進行排序、二分查詢、反轉、混排等。
Collection和Collections的區別
Collection是單列集合的頂層介面,有子介面List和Set;而
工具教程第三十一講:電報的使用(二)
這裡是王團長區塊鏈學院,與最優秀的區塊鏈人一起成長!今天給大家講講電報Telegram的使用。
第三步、註冊使用Telegram
1、點開telegram,點選開始
2、在選擇國家處選擇中國China,填寫手機號碼,最後點“√”進入下一步
愛創課堂每日一題第三十二天-談談浮動和清除浮動?
前端 前端學習 前端入門浮動的框可以向左或向右移動,直到他的外邊緣碰到包含框或另一個浮動框的邊框為止。由於浮動框不在文檔的普通流中,所以文檔的普通流的塊框表現得就像浮動框不存在一樣。浮動的塊框會漂浮在文檔普通流的塊框上。愛創課堂每日一題第三十二天-談談浮動和清除浮動?
mysql 第三十二篇文章~並發導致的從庫延遲問題
efault nbsp 記錄 文章 定位 如果 增刪查改 mysql binlog 一 簡介:今天來聊聊周期性從庫延遲的問題
二 背景:近期每天的指定時間段,收到從庫延遲的報警,然後過一段時間恢復.由於從庫是提供讀服務的,所以需要解決
三 分析思路:
學習筆記第三十二節課
作業iptables規則備份和恢復。
service iptables save 會把規則保存到 /etc/sysconfig/iptables配置文件中,但是有時候不想保存這個位置。
可以用命令 iptables-sabe > 到你想保存的位置。
恢復備份的規則的話 是iptables-re
第三十二篇 玩轉數據結構——AVL樹
ces this true 函數 port ide cep row ger
1.. 平衡二叉樹
平衡二叉樹要求,對於任意一個節點,左子樹和右子樹的高度差不能超過1。
平衡二叉樹的高度和節點數量之間的關系也是O(logn)
為二叉樹標註節點高度並計算平
SpringBoot | 第三十二章:事件的釋出和監聽
前言
今天去官網檢視spring boot資料時,在特性中看見了系統的事件及監聽章節。想想,spring的事件應該是在3.x版本就釋出的功能了,並越來越完善,其為bean和bean之間的訊息通訊提供了支援。比如,我們可以在使用者註冊成功後,傳送一份註冊成功的郵件至使用者郵箱或者傳送簡訊。使用事件其實最
C++筆記 第三十二課 初探C++標準庫---狄泰學院
如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux
第三十二課 初探C++標準庫
1.有趣的過載
操作符<<的原生意義是按位左移,例:1<<2; 其意義是將整數1按位左移2位,即:0000 0001 -
名詞解釋第七十二講:軟分叉
這裡是王團長區塊鏈學院,與最優秀的區塊鏈人一起成長!今天給大家講講軟分叉。
區塊鏈上節點眾多,當出現新版軟體後,不一定所有的節點都願意升級,也不一定所有的節點都能馬上反應過來。節點面對新版軟體反應的不同,造成的分叉結果也不同。
根據升級後的區塊鏈是否能相容
Scrum立會報告+燃盡圖(十一月二十四日總第三十二次):視訊剪輯
此作業要求參見:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284
專案地址:https://git.coding.net/zhangjy982/QuJianBang.git
Scrum立會master:李文濤
一、小組介
Scrum立會報告+燃盡圖(十一月二十四日總第三十二次):展示部落格
此作業要求參見:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2413
專案地址:https://git.coding.net/zhangjy982/QuJianBang.git
Scrum立會master:段曉睿
一、小組介紹
組長:付佳
Scrum立會報告+燃盡圖(十一月二十四日總第三十二次):展示博客
程序 odin ext board hang lis 介紹 圖片 分析 此作業要求參見:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2413
項目地址:https://git.coding.net/zhangj
第四十二講 I/O流——位元組流在操作中文資料
本篇文章主要圍繞字元編碼展開,為了能夠更好地講述這一主題,我將從位元組流操作中文資料開始。
位元組流操作中文資料
假設編寫有如下程式,程式碼貼出如下:
package cn.liayun.readcn;
import java.io.FileOutputStream;
im
第三十七講 JDK1.5新特性
高階for迴圈
高階for迴圈的作用是用於遍歷Collection集合或陣列。其格式為:
for(資料型別(一般是泛型型別) 變數名 : 被遍歷的集合(Collection)或者陣列) {
}
遍歷Collection集合
之前我們使用迭代器是這樣遍歷的。
pac
第三十四講 初次認識泛型
泛型的簡單概述
泛型是JDK1.5版本以後出現的新特性。它用於解決安全問題,是一個型別安全機制。
泛型的由來
概念說完之後,我們來看看Java語言是如何引入泛型的。在JDK1.4版本之前,容器什麼型別的物件都可以儲存,但是在取出時,需要用到物件的特有內容時,這時需要做向下轉型
第三十二天- 管道 程序池
1.管道
程序間通訊(IPC)方式二:管道(不推薦使用,瞭解即可),埠易導致資料不安全的情況出現。
1 from multiprocessing import Pipe,Process
2
3
4 def func(conn1,conn2):
5 msg =
學習筆記第三十二節:線性規劃與單純形
正題
我們今天講一下線性規劃,以這一道題為例:#179. 線性規劃
首先面對一堆小於等於的約束,我們應該怎麼做?
我們以樣例來解釋:
&nb
“全棧2019”Java第三十二章:增強for迴圈Foreach語法
難度
初級
學習時間
10分鐘
適合人群
零基礎
開發語言
Java
開發環境
JDK v11
IntelliJ IDEA v2018.3
文章原文連結
“全棧2019”Java第三十二章:增強for迴圈Foreach語法