Java集合之四Set、HashSet、LinkedHashSet、TreeSet
Set
Set介面是Collection的子介面,set介面沒有提供額外的方法。
Set 集合不允許包含相同的元素,最多允許一個null值,如果試把兩個相同的元素加入同一個 Set 集合中,則新增操作失敗。Set 判斷兩個物件是否相同不是使用 == 運算子,而是根據 equals 方法。
Set集合是如何判斷兩個元素是否相等的?
是通過物件的hashCode()方法和equals()方法判斷的。所以建議自定義類要重寫hashCode()方法和equals()方法。
首先Set集合是通過雜湊演算法來儲存元素的,當像Set中新增物件時,首先呼叫此物件所在類的hashCode()方法,計算此物件的雜湊值,此雜湊值決定了此物件在Set中儲存的位置。若此位置沒有物件儲存,則直接把物件儲存進來,如果此位置已經有一個物件了,則通過equals()方法比較這兩個物件是否相同,如果不同則儲存進去,如果相同則這個物件不能儲存進Set裡。所以我們重寫hashCode()方法和equals()方法,還要保持這兩個方法一致,即hashCode相同的物件equals也要相同。
Set 集合判斷兩個元素相等的標準:兩個物件通過 hashCode() 方法比較相等,並且兩個物件的 equals() 方法返回值也相等。
重寫 hashCode() 方法的基本原則
在程式執行時,同一個物件多次呼叫 hashCode() 方法應該返回相同的值
當兩個物件的 equals() 方法比較返回 true 時,這兩個物件的 hashCode() 方法的返回值也應相等
物件中用作 equals() 方法比較的 Field,都應該用來計算 hashCode 值
下面是一個沒有重寫hashCode()方法和equals()方法的例子
public class Person { private String name; private int age; public Person(String name,int age){ this.name = name; this.age = 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; } }
測試類
輸出結果@Test public void testSet(){ Set set = new HashSet(); Person p1 = new Person("jack",10); Person p2 = new Person("jack",10); set.add(p1); set.add(p2); System.out.println(p1.hashCode()); System.out.println(p2.hashCode()); System.out.println(p1.equals(p2)); System.out.println(set); }
99550389
1598924227
false
[[email protected], [email protected]]
所以我們希望第二個物件不能儲存進去的話就必須重寫,現在的hashCode()方法和equals()方法都是用的Object的方法。
現在重寫hashCode()方法和equals()方法
public class Person {
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = 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 int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
輸出結果
3255510
3255510
true
[[email protected]]
HashSet
HashSet集合內的元素是無序的,注意並不是隨機性。它是按 Hash 演算法來儲存集合中的元素,因此具有很好的存取和查詢效能。
HashSet 具有以下特點:
①不能保證元素的排列順序
②HashSet 不是執行緒安全的,如果需要多執行緒訪問它的話,可以用 Collections.synchronizedSet 方法來包裝它:
Set s = Collections.synchronizedSet(new HashSet(...));
③集合元素可以是 null
LinkedHashSet
LinkedHashSet 是 HashSet 的子類,不允許集合元素重複。LinkedHashSet 根據元素的 hashCode 值來決定元素的儲存位置,但它同時使用連結串列維護元素的次序,這使得元素看起來是以插入順序儲存的。LinkedHashSet插入效能略低於 HashSet,但在迭代訪問 Set 裡的全部元素時有很好的效能。
TreeSet
TreeSet 是 SortedSet 介面的實現類,TreeSet 可以確保集合元素處於排序狀態。
特點:①TreeSet裡新增的元素必須是同一型別的。
②可以按照新增進集中中的元素的指定順序遍歷,如String類、包裝類預設按照從小到大的順序遍歷。
③當像TreeSet內新增自定義類的物件時,可以有兩種排序方法:1.自然排序2.定製排序
自然排序
TreeSet 會呼叫集合元素的 compareTo(Object obj) 方法來比較元素之間的大小關係,然後將集合元素按升序排列。如果試圖把一個物件新增到 TreeSet 時,則該物件的類必須實現 Comparable 介面。實現 Comparable 的類必須實現 compareTo(Object obj) 方法,兩個物件即通過 compareTo(Object obj) 方法的返回值來比較大小。
當需要把一個物件放入 TreeSet 中,重寫該物件對應的 equals() 時,應保證與 compareTo(Object obj) 方法有一致的結果:如果兩個物件通過 equals() 方法比較返回 true,則通過 compareTo(Object obj) 方法比較應返回 0,要求equals()方法和hashCode()方法和compareTo()方法保持一致。
public class Person implements Comparable {
private String name;
private Integer age;
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p = (Person)o;
int result = this.age.compareTo(p.getAge());
if(result == 0){
result = this.name.compareTo(p.getName());
}
return result;
}
return 0;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
測試類
@Test
public void testTreeSet(){
Set set = new TreeSet();
set.add(new Person("jack",10));
set.add(new Person("jack",20));
set.add(new Person("abc",10));
set.add(new Person("tom",15));
set.add(new Person("abc",10));
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
輸出結果
Person [name=abc, age=10]
Person [name=jack, age=10]
Person [name=tom, age=15]
Person [name=jack, age=20]
定製排序TreeSet的自然排序是根據集合元素的大小,進行元素升序排列。如果需要定製排序,比如降序排列,可通過Comparator介面的幫助。需要重寫compare(To1,T o2)方法。利用int compare(To1,T o2)方法,比較o1和o2的大小:如果方法返回正整數,則表示o1大於o2;如果返回0,表示相等;返回負整數,表示o1小於o2。要實現定製排序,需要將實現Comparator介面的例項作為形參傳遞給TreeSet的構造器。此時,仍然只能向TreeSet中新增型別相同的物件。否則發生ClassCastException異常。使用定製排序判斷兩個元素相等的標準是:通過Comparator比較兩個元素返回了0,要求equals()方法和hashCode()方法和compareTo()方法保持一致。。
public class Person {
private Integer id;
private String name;
private Integer age;
public Person(Integer id,String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
@Test
public void test1(){
//建立一個實現Comparator介面的物件
Comparator com = new Comparator() {
//重寫compare方法,定義Person類的排序規則,這裡定義按id從小到大排序
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o2 instanceof Person){
Person p1 = (Person)o1;
Person p2 = (Person)o2;
return p1.getId().compareTo(p2.getId());
}
return 0;
}
};
//把com物件當做形參傳到TreeSet構造器中
TreeSet set = new TreeSet(com);
set.add(new Person(1,"jack",12));
set.add(new Person(21,"jack",12));
set.add(new Person(11,"jack",12));
set.add(new Person(9,"jack",12));
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
輸出結果Person [id=1, name=jack, age=12]
Person [id=9, name=jack, age=12]
Person [id=11, name=jack, age=12]
Person [id=21, name=jack, age=12]
下面看一個加強版
定義一個Employee類,該類包含:private成員變數name,age,birthday,其中 birthday 為 MyDate 類的物件;定義一個MyDate類包含:private成員變數month,day,year;
分別按以下兩種方式對集合中的元素進行排序,並遍歷輸出:
1). 使Employee實現Comparable 介面,並按 name 排序
2). 建立TreeSet 時傳入 Comparator物件,按生日日期的先後排序。
注意:Employee類和MyDate類都要重寫hashCode()方法和equals()方法public class Employee implements Comparable<Employee>{
private String name;
private Integer age;
private MyDate birthDay;
public Employee(String name, Integer age, MyDate birthDay) {
super();
this.name = name;
this.age = age;
this.birthDay = birthDay;
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + ", birthDay=" + birthDay + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public MyDate getBirthDay() {
return birthDay;
}
public void setBirthDay(MyDate birthDay) {
this.birthDay = birthDay;
}
@Override
public int compareTo(Employee o) {
return this.getName().compareTo(o.getName());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
result = prime * result + ((birthDay == null) ? 0 : birthDay.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age == null) {
if (other.age != null)
return false;
} else if (!age.equals(other.age))
return false;
if (birthDay == null) {
if (other.birthDay != null)
return false;
} else if (!birthDay.equals(other.birthDay))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class MyDate {
private Integer year;
private Integer month;
private Integer day;
public MyDate(Integer year, Integer month, Integer day) {
super();
this.year = year;
this.month = month;
this.day = day;
}
@Override
public String toString() {
return "MyDate [year=" + year + ", month=" + month + ", day=" + day + "]";
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getMonth() {
return month;
}
public void setMonth(Integer month) {
this.month = month;
}
public Integer getDay() {
return day;
}
public void setDay(Integer day) {
this.day = day;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((day == null) ? 0 : day.hashCode());
result = prime * result + ((month == null) ? 0 : month.hashCode());
result = prime * result + ((year == null) ? 0 : year.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyDate other = (MyDate) obj;
if (day == null) {
if (other.day != null)
return false;
} else if (!day.equals(other.day))
return false;
if (month == null) {
if (other.month != null)
return false;
} else if (!month.equals(other.month))
return false;
if (year == null) {
if (other.year != null)
return false;
} else if (!year.equals(other.year))
return false;
return true;
}
}
/**
* 使用自然排序 根據name排序
*/
@Test
public void test2(){
TreeSet<Employee> set = new TreeSet<Employee>();
set.add(new Employee("abc", 12, new MyDate(2013, 10, 1)));
set.add(new Employee("adc", 13, new MyDate(2013, 10, 1)));
set.add(new Employee("jwbc", 23, new MyDate(2013, 10, 1)));
set.add(new Employee("c2bc", 35, new MyDate(2013, 10, 1)));
set.add(new Employee("wdabc", 55, new MyDate(2013, 10, 1)));
set.add(new Employee("abc", 12, new MyDate(2013, 10, 1)));
Iterator<Employee> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
/**
* 使用定製排序,根據生日先後排序
*/
@Test
public void test3(){
Comparator<Employee> com = new Comparator<Employee>() {
/**
* 這樣寫排序規則後,相同的生日的Employee會插不進來,所以你也可以再加上判斷name和age的規則
* 這裡就只對生日進行排序了,TreeSet會先後呼叫compare方法、hashCode方法、equals方法來
* 判斷插進來的物件是否存在於Set中。
* @param o1
* @param o2
* @return
*/
@Override
public int compare(Employee o1, Employee o2) {
MyDate birth1 = o1.getBirthDay();
MyDate birth2 = o2.getBirthDay();
if(!birth1.getYear().equals(birth2.getYear())){
return birth1.getYear().compareTo(birth2.getYear());
}else{
if(!birth1.getMonth().equals(birth2.getMonth())){
return birth1.getMonth().compareTo(birth2.getMonth());
}else{
return birth1.getDay().compareTo(birth2.getDay());
}
}
}
};
TreeSet<Employee> set = new TreeSet<Employee>(com);
set.add(new Employee("abc", 12, new MyDate(1913, 10, 1)));
set.add(new Employee("adc", 13, new MyDate(2013, 8, 7)));
set.add(new Employee("jwbc", 23, new MyDate(2000, 4, 23)));
set.add(new Employee("c2bc", 35, new MyDate(1993, 1, 11)));
set.add(new Employee("wdabc", 55, new MyDate(2017, 12, 9)));
set.add(new Employee("abc", 12, new MyDate(2013, 11, 1)));
set.add(new Employee("abc", 12, new MyDate(2013, 11, 1)));
Iterator<Employee> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
test2輸出結果
Employee [name=abc, age=12, birthDay=MyDate [year=2013, month=10, day=1]]
Employee [name=adc, age=13, birthDay=MyDate [year=2013, month=10, day=1]]
Employee [name=c2bc, age=35, birthDay=MyDate [year=2013, month=10, day=1]]
Employee [name=jwbc, age=23, birthDay=MyDate [year=2013, month=10, day=1]]
Employee [name=wdabc, age=55, birthDay=MyDate [year=2013, month=10, day=1]]
test3輸出結果
Employee [name=abc, age=12, birthDay=MyDate [year=1913, month=10, day=1]]
Employee [name=c2bc, age=35, birthDay=MyDate [year=1993, month=1, day=11]]
Employee [name=jwbc, age=23, birthDay=MyDate [year=2000, month=4, day=23]]
Employee [name=adc, age=13, birthDay=MyDate [year=2013, month=8, day=7]]
Employee [name=abc, age=12, birthDay=MyDate [year=2013, month=11, day=1]]
Employee [name=wdabc, age=55, birthDay=MyDate [year=2017, month=12, day=9]]
相關推薦
Java集合之四Set、HashSet、LinkedHashSet、TreeSet
Set Set介面是Collection的子介面,set介面沒有提供額外的方法。 Set 集合不允許包含相同的元素,最多允許一個null值,如果試把兩個相同的元素加入同一個 Set 集合中,則新增操作失敗。 Set 判斷兩個物件是否相同不是使用 == 運算子,而是根據 eq
Java集合框架:Set(HashSet,LinkedHashSet,TreeSet)
Set概述 Set幾乎都是內部用一個Map來實現, 因為Map裡的KeySet就是一個Set,而value是假值,全部使用同一個Object。Set的特徵也繼承了那些內部Map實現的特徵。 HashSet 1. 定義 package java.util; p
Java集合之HashSet、LinkedHashSet、TreeSet
討論集合關注的問題: 底層資料結構 增刪改查方式 初始容量,擴容方式,擴容時機 執行緒安全與否 是否允許空,是否允許重複,是否有序 1. 概述 前篇,我寫了關於Map系列的集合(點選跳轉);本篇重新回顧Coll
java集合之列表:ArrayList、Vector、LinkedList
sta pop arraylist 允許 dex nsa pack java jdk 1 package com.jdk7.chapter4; 2 3 import java.util.ArrayList; 4 import java.util.Link
【Java集合之Map】HashMap、HashTable、TreeMap、LinkedHashMap區別
前言 Java為資料結構中的對映定義了一個介面java.util.Map,它有四個實現類,分別是HashMap、HashTable、LinkedHashMap和TreeMap。本節例項主要介紹這4中例項的用法和區別 幾種Map類結構 public clas
Java-Collection原始碼分析(十二)——Set、AbstractSet、HashSet和LinkedHashSet
該類提供了Set介面的骨架實現,以最大限度地減少實現此介面所需的工作量。 通過擴充套件此類來實現集合的過程與通過擴充套件AbstractCollection實現集合的過程相同,除了此類的子類中的所有方法和建構函式都必須遵守由Set介面施加的附加約束(例如,新增方法不能允許將一個物件的多個例項新增到集合中)。
Java集合系列(四):HashMap、Hashtable、LinkedHashMap、TreeMap的使用方法及區別
本篇部落格主要講解Map介面的4個實現類HashMap、Hashtable、LinkedHashMap、TreeMap的使用方法以及三者之間的區別。 注意:本文中程式碼使用的JDK版本為1.8.0_191 值得注意的是,Map介面是獨立的介面,並沒有繼承Collection介面(這裡是重點,面試常問):
【Java集合系列四】HashSet和LinkedHashSet解析
inpu skin lam 繼承 depend try put args port 2017-07-29 16:58:13 一、簡介 1、Set概念 Set可以理解為集合,非常類似數據概念中的集合,集合三大特征:1、確定性;2、互異性;3、無序性,因此Set實現類也有類似的
java集合之set
ash 字符數組 his err new rgs return 清除 single 1 public class Demo1_Set { 2 3 /* 4 * set集合無序、不可重復、無索引 5 */ 6 p
Java 集合之Set簡介
返回值 接口模擬 區別 key mov 無序 刪除 tor 可能 一.Set集合Set:不包含重復元素的集合。 更正式地,集合不包含一對元素e1和e2 ,使得e1.equals(e2) ,並且最多一個空元素。正如其名稱所暗示的那樣,這個接口模擬了數學集抽象。一些集合實現對它
Java 集合之HashSet常用方法實例介紹
java args arr 子類 boolean 常用 地址 比例 可能 一.簡介HashSet是Set常見的子類對象,此類實現Set接口,由哈希表(實際為HashMap實例)支持。 對集合的叠代次序不作任何保證; 特別是,它不能保證訂單在一段時間內保持不變。這個類允許nu
java集合遍歷中的向下轉型、泛型
java中集合儲存字串時,集合的get(i)方法是獲取集合中的第i+1個元素,而這個元素是Object型別,而Object型別沒有length()方法,遍歷的時候如果直接.length()會報錯。如果想使用字串的方法,就必須把元素還原成字元(向下轉型)。 集合的遍歷。其實就是依次獲取集合
Java中的四種引用型別(強、軟、弱、虛)
為什麼需要不同的引用型別 從Java1.2開始,JVM開發團隊發現,單一的強引用型別,無法很好的管理物件在JVM裡面的生命週期,垃圾回收策略過於簡單,無法適用絕大多數場景。為了更好的管理物件的記憶體,更好的進行垃圾回收,JVM團隊擴充套件了引用型別,從最早的強引用型別增加到強、軟、弱、虛四個引用
Java練習之用Set集合管理課程
說明: 1.提供備選課程。 2.建立學生物件,並給學生新增三門課程(新增在學生的courses—set集合中)。要求能夠顯示備選課程,迴圈三次,每次輸入課程ID,並向學生的courses屬性中新增與輸入ID相匹配的課程,最後輸出學生所選的課程。 public class SetTest
(Java)集合框架(一)Collection介面方法、Iterator迭代器、增強for迴圈
【Collection介面】 import java.util.ArrayList; import java.util.Collection; /* * Collection介面中的方法 是集合中所有實現類必須擁有的方法 * 程式演示,使用Collection
Java語句之順序結構&選擇結構(if、switch)&迴圈結構(for、while)
Java中,語句分為三大類,順序結構,選擇結構(if語句、switch語句),迴圈結構(for語句、while語句)。 一、順序結構 就是按照輸出語句來 例: System.out.printl
[瘋狂Java]集合:Collection的迭代器Iterator、使用Predicate篩選集合中的元素
1. Iterator——迭代器: 1) 和C++中迭代器的概念一樣,二要素: i. 迭代器必定從屬於某個容器,其作用就是用來遍歷所屬容器中的元素的! ii. 迭代器是在容器的資料檢視之上進行迭代,因此不能再迭代過程中修改容器中的
JAVA集合 Deque 與 Queue 實現類 ArrayDeque(佇列、雙端佇列) 原始碼淺析
文章目錄 JAVA集合 Deque實現類 ArrayDeque(雙端佇列) 原始碼淺析 一、簡述: 二、ArrayDeque 類結構與屬性 三、ArrayDeque 構造方法 四、Queue 的方法 1.
Java 集合之 Set 詳解與原始碼分析
Set集合與List一樣,都是繼承自Collection介面,常用的實現類有HashSet和TreeSet。值得注意的是,HashSet是通過HashMap來實現的而TreeSet是通過TreeMap來實現的,所以HashSet和TreeSet都沒有自己的資料結構,具
死磕 java集合之TreeMap源碼分析(四)-內含彩蛋
留言 cti 簡單 刪除元素 回顧 over foreach hub rst 歡迎關註我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢遊源碼的海洋。 二叉樹的遍歷 我們知道二叉查找樹的遍歷有前序遍歷、中序遍歷、後序遍歷。 (1)前序遍歷,先遍歷我,再遍歷我的