1 Set接口簡介
Set集合最大的特點就是不允許保存重複元素,其也是Collection的子接口。
在JDK 1.9以前與Collection集合的定義並無差別,Set繼續使用了Collection接口中提供的方法進行操作,但是從JDK 1.9開始,Set集合也像List集合一樣擴充了一些static方法,Set集合的定義如下:
public interface Set<E> extends Collection<E>
需要注意的是Set集合並不像List集合那樣擴充了許多的新方法,所以無法使用List集合中提供的get()方法,也就是說它無法實現指定索引數據的獲取,Set接口的繼承關係如下:
從JDK 1.9之後,Set集合也提供像List集合之中類似的of()的靜態方法,下面就使用此方法進行Set集合特點的驗證。
範例:驗證Set集合特徵
package org.lks.demo;
import java.util.Set;
public class JavaReflectDemo {
public static void main(String[] args) {
Set<String> set = Set.of("hello", "world", "hello", "world");
set.forEach(System.out::println);
}
}
/*
Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: hello
*/
當使用of新方法的時候,如果發現集合之中存在有重複元素則會直接拋出異常。這與傳統的Set集合不保存重複元素的特點相一致,只不過自己拋出了異常而已。
Set集合的常規使用形式一定是依靠子類進行實例化的,所以Set接口之中有兩個常用子類:HashSet、TreeSet。
2 HashSet子類
HashSet是Set接口裏面使用最多的一個子類,其最大的特點就是保存的數據是無序的,而HashSet子類的繼承關係如下:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable
這種繼承的形式和之前的ArrayList是非常相似的,那麼現在來觀察一下類的繼承結構:
範例:觀察HashSet子類
package org.lks.demo;
import java.util.HashSet;
import java.util.Set;
public class JavaReflectDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("hello");
set.add("world");
set.add("hello");
set.forEach(System.out::println);
}
}
通過執行結果就可以發現HashSet子類的操作特點:不允許保存重複元素(St接口定義的),另外一點HashSet之中保存的數據是無序的。
3 TreeSet子類
Set接口的另外一個子類就是TreeSet,與HashSet最大的區別在於TreeSet集合裏面保存的數據是有序的,首先來觀察一下TreeSet類的定義:
public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable
通過觀察我們看出TreeSet也繼承了AbstractSet父抽象類,同時實現了一個NavigableSet父接口。
範例:使用TreeSet子類
package org.lks.demo;
import java.util.Set;
import java.util.TreeSet;
public class JavaReflectDemo {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>();
set.add("hello");
set.add("world");
set.add("hello");
set.forEach(System.out::println);
}
}
當利用TreeSet保存的數據的時候所有的數據都將按照數據的升序進行自動的排序處理。
4 分析TreeSet子類排序操作
經過分析之後,發現TreeSet子類之中保存的數據是允許排序的,但是這個類必須要實現Comparable接口,因爲只有實現了此接口才能夠確認出對象的大小關係,那麼下面就使用一個定義的類來實現排序的處理操作。
範例:實現自定義排序
package org.lks.demo;
import java.util.Set;
import java.util.TreeSet;
class PersonA implements Comparable<PersonA>{
private String name;
private int age;
public PersonA() {}
public PersonA(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "PersonA [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(PersonA per) {
if( this.age > per.age) {
return 1;
}else if(this.age < per.age){
return -1;
}else {
return this.name.compareTo(per.name);
}
}
}
public class JavaReflectDemo {
public static void main(String[] args) {
Set<PersonA> set = new TreeSet<PersonA>();
set.add(new PersonA("lks", 23));
set.add(new PersonA("hhy", 20));
set.add(new PersonA("zsl", 22));
set.add(new PersonA("sss", 22));
set.add(new PersonA("hhy", 20));
set.forEach(System.out::println);
}
}
TreeSet本質是利用TreeMap子類實現的集合數據的存儲,而TreeMap(樹)就需要根據Comparable來確定大小關係。
在使用自定義類對象進行比較處理的時候一定要將該類之中的所有屬性都依次進行大小關係的匹配,否則如果某一個或某幾個屬性相同的時候它也會認爲是重複數據,所以TreeSet是利用了Comparable接口來確認重複數據的。
由於TreeSet在操作過程之中需要將類中所有的屬性進行比對,這樣的實現難度太高了,那麼在實際的開發之中應該首選HashSet子類進行存儲。
5 分析重複元素消除
TreeSet子類是利用了Comparable接口來實現了重複元素的判斷,但是Set集合的整體特徵就是不允許保存重複元素。但是HashSet判斷重複元素的方式並不是利用Comparable接口完成的,他利用的是Object類中提供的方法實現的。
(1)對象編碼:public int hashCode()
;
(2)對象比較:public boolean equals(Object obj)
;
在進行重複元素判斷的時候首先利用hashCode()進行編碼的匹配,如果該編碼不存在則表示數據不存在,證明沒有重複,如果該編碼存在了,則進一步進行對象比較處理,如果發現重複了,則此數據是不允許保存的。如果使用的是Eclipse開發工具,則可以幫助開發者自動創建hashCode()與equals()方法。
範例:實現重複元素的處理
package org.lks.demo;
import java.util.HashSet;
import java.util.Set;
class PersonA{
private String name;
private int age;
public PersonA() {}
public PersonA(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "PersonA [name=" + name + ", 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;
PersonA other = (PersonA) 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;
}
}
public class JavaReflectDemo {
public static void main(String[] args) {
Set<PersonA> set = new HashSet<PersonA>();
set.add(new PersonA("lks", 23));
set.add(new PersonA("hhy", 20));
set.add(new PersonA("zsl", 22));
set.add(new PersonA("sss", 22));
set.add(new PersonA("hhy", 20));
set.forEach(System.out::println);
}
}
在Java程序之中真正的重複元素的判斷處理利用的就是hashCode與equals()兩個方法共同作用完成的,而只有在排序要求的情況下(TreeSet)纔會利用Comparable1接口來實現。