一蝉衣、Set集合概述和特點(diǎn)
1. Set集合概述和特點(diǎn)
java.util.Set 接口和 java.util.List 接口一樣艘绍,同樣繼承自 Collection 接口嚷量,它與 Collection 接口中的方法基本一致孩革,并沒有對(duì) Collection 接口進(jìn)行功能上的擴(kuò)充掂摔,只是比 Collection 接口更加嚴(yán)格了说莫。與List 接口不同的是杨箭,Set 接口中元素?zé)o序,并且都會(huì)以某種規(guī)則保證存入的元素不出現(xiàn)重復(fù)唬滑。Set 集合有多個(gè)子類告唆,這里我們介紹其中的 HashSet 、LinkedHashSet及TreeSet這三個(gè)集合晶密。
二擒悬、集合 HashSet
1.HashSet類概述
java.util.HashSet 是 Set 接口的一個(gè)實(shí)現(xiàn)類,它所存儲(chǔ)的元素是不可重復(fù)的稻艰,并且元素都是無序的(即存取順序不一致)懂牧。 java.util.HashSet底層的實(shí)現(xiàn)其實(shí)是一個(gè)java.util.HashMap 支持,由于我們暫時(shí)還未學(xué)習(xí)尊勿,先做了 解僧凤。
? HashSet 是根據(jù)對(duì)象的哈希值來確定元素在集合中的存儲(chǔ)位置,因此具有良好的存取和查找性能元扔。保證元素唯一性的方式依賴于: hashCode 與 equals 方法躯保。如果我們往集合中存放自定義的對(duì)象,那么保證其唯一澎语,就必須復(fù)寫hashCode和equals方法建立屬于當(dāng)前對(duì)象的比較方式途事。
6.2.2 HashSet使用-去重
1. 存儲(chǔ)字符串并遍歷
public class Test {
public static void main(String[] args) {
// Set是接口,創(chuàng)建對(duì)象new 的是 實(shí)現(xiàn)類HashSet 是其中一個(gè)實(shí)現(xiàn)類
Set<String> set = new HashSet<String>();
//給集合 添加元素
set.add("Hello1");
set.add("Hello1");
set.add("Hello2");
set.add("Hello3");
set.add("Hello4");
//增強(qiáng)for遍歷
for (String string : set) {
//不含有重復(fù)元素 Hello1
//且存取順序不一致
System.out.println(string);
}
}
}
運(yùn)行結(jié)果:
2. 存儲(chǔ)自定義對(duì)象并遍歷
public class Test {
public static void main(String[] args) {
// Set是接口,創(chuàng)建對(duì)象new的是實(shí)現(xiàn)類,HashSet 是其中一個(gè)實(shí)現(xiàn)類
Set<Student> set = new HashSet<Student>();
//給集合 添加元素
set.add(new Student(15, "小李"));
set.add(new Student(15, "小王"));
set.add(new Student(15, "小王"));
set.add(new Student(15, "小王"));
set.add(new Student(15, "小王"));
//增強(qiáng)for遍歷
for (Student s : set) {
System.out.println(s);
}
}
//自定義類
public static class Student {
int age;
String name;
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
//使用的是HashSet實(shí)現(xiàn)類,由 hashCode 和 equals 保證唯一
// 如果是自定義類,作為元素要重寫 hashCode 和 equals 方法
@Override
public int hashCode() {
//調(diào)用工具類,獲取當(dāng)前對(duì)象的hashcode
return Objects.hash(name, age);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
//對(duì)象地址值一樣,則是同一個(gè)對(duì)象
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
//年齡不相同,則不是同一個(gè)對(duì)象
return false;
if (name == null) {
if (other.name != null)
//比較的對(duì)象名字是null,而被比較的不是,則不是同一個(gè)對(duì)象
return false;
} else if (!name.equals(other.name))
//name不相同,不是同一個(gè)對(duì)象
return false;
return true;
}
}
}
運(yùn)行結(jié)果:
三擅羞、集合 LinkedHashSet
6.3.1 LinkedHashSet類概述和特點(diǎn)
我們知道HashSet保證元素唯一尸变,可是元素存放進(jìn)去是沒有順序的,那么我們要保證有序减俏,怎么辦呢? 在HashSet下面有一個(gè)子類 java.util.LinkedHashSet 召烂,它是鏈表和哈希表組合的一個(gè)數(shù)據(jù)存儲(chǔ)結(jié)構(gòu),由鏈表保證元素有序,由哈希表保證元素唯一娃承。
1. 存儲(chǔ)字符串并遍歷
public class Test {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<String>();
//給集合 添加元素
set.add("Hello1");
set.add("Hello1");
set.add("Hello2");
set.add("Hello3");
set.add("Hello4");
//增強(qiáng)for 遍歷
for (String string : set) {
/*
* Hello1
Hello2
Hello3
Hello4
去掉重復(fù)的
存取有序
*/
System.out.println(string);
}
}
}
運(yùn)行效果:
2. 存儲(chǔ)自定義對(duì)象并遍歷
public class Test {
public static void main(String[] args) {
LinkedHashSet<Student> set = new LinkedHashSet<Student>();
//給集合 添加元素
set.add(new Student(15, "小李"));
set.add(new Student(15, "小王"));
set.add(new Student(16, "小王"));
set.add(new Student(15, "小王"));
set.add(new Student(15, "小王"));
//增強(qiáng)for遍歷
for (Student s : set) {
System.out.println(s);
}
}
//自定義類
public static class Student {
int age;
String name;
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
//使用的是HashSet實(shí)現(xiàn)類,由 hashCode 和 equals 保證唯一
//如果是自定義類,作為元素要重寫 hashCode 和 equals 方法
@Override
public int hashCode() {
//調(diào)用工具類,獲取當(dāng)前對(duì)象的hashcode
return Objects.hash(name, age);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
//對(duì)象地址值一樣,則是同一個(gè)對(duì)象
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
//年齡不相同,則不是同一個(gè)對(duì)象
return false;
if (name == null) {
if (other.name != null)
//比較的對(duì)象名字是null,而被比較的不是,則不是同一個(gè)對(duì)象
return false;
} else if (!name.equals(other.name))
//name不相同,不是同一個(gè)對(duì)象
return false;
return true;
}
}
}
運(yùn)行效果:
四奏夫、集合 TreeSet
1. TreeSet類概述和特點(diǎn)
TreeSet是一個(gè)有序的集合怕篷,它的作用是提供有序的Set集合。它繼承了AbstractSet抽象類桶蛔,實(shí)現(xiàn)了NavigableSet<E>匙头,Cloneable,Serializable接口仔雷。TreeSet是基于TreeMap實(shí)現(xiàn)的蹂析,TreeSet的元素支持2種排序方式:自然排序或者根據(jù)提供的Comparator進(jìn)行排序。
特點(diǎn)如下:
? ①元素唯一且有序碟婆;
? ②支持2種排序方式:自然排序或者根據(jù)提供的Comparator進(jìn)行排序电抚。
? ③底層數(shù)據(jù)結(jié)構(gòu)是紅黑樹(紅黑樹是一種自平衡的二叉樹)
2. TreeSet是如何保證元素的排序和唯一性的
TreeSet底層是通過TreeMap實(shí)現(xiàn)的。它把Map中的Key作為一類元素歸結(jié)起來竖共,而Map的Key是唯一的蝙叛。Map我們后面會(huì)學(xué)習(xí)到。
3. TreeSet的自然排序和比較器排序
1. 自然排序
a.定義一個(gè)類(示例是Student)實(shí)現(xiàn)Comparable接口公给,不實(shí)現(xiàn)會(huì)報(bào)異常 java.lang.ClassCastException: com.xts.im.test.Test$Student cannot be cast to java.lang.Comparable
b.重寫Comparable接口中的compareTo()方法;
c.在compareTo()中按指定屬性進(jìn)行排序(示例中先按年齡借帘,如果年齡一樣按姓名排序)
示例代碼如下:
public class Test {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<>();
//給集合 添加元素
set.add(new Student(100, "小張"));
set.add(new Student(15, "小王"));
set.add(new Student(15, "小李"));
set.add(new Student(15, "小王"));
set.add(new Student(16, "小王"));
//增強(qiáng)for遍歷
for (Student s : set) {
System.out.println(s);
}
}
//自定義類,實(shí)現(xiàn)了Comparable接口
public static class Student implements Comparable{
int age;
String name;
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
@Override
public int compareTo(@NonNull Object obj) {
Student student = (Student) obj;
//1.優(yōu)先以年齡排序
//2.年齡一樣使用name排序
//先比較年齡
int a = this.age - student.getAge();
//System.out.println(this.age);
//System.out.println(student.getAge());
//System.out.println(a);
//次要排序條件name
//如果年齡一樣就比較姓名
if(a==0){
a = this.name.compareTo(student.getName());
}
// 根據(jù)a是 負(fù)整數(shù)、零或正整數(shù)淌铐,此對(duì)象是小于肺然、等于還是大于指定對(duì)象
//a是0:當(dāng)前對(duì)象和obj一樣,不存儲(chǔ)
//a是正整數(shù):當(dāng)前對(duì)象比obj大,在二叉樹中,obj排在當(dāng)前對(duì)象的左邊節(jié)點(diǎn)
//a是負(fù)整數(shù):當(dāng)前對(duì)象比obj小,在二叉樹中,obj排在當(dāng)前對(duì)象的右邊節(jié)點(diǎn)
return a;
}
}
}
2. 比較器排序
a.創(chuàng)建一個(gè)比較器類,實(shí)現(xiàn)Comparator接口腿准,重寫compare()方法际起;
b.在compare()中按指定屬性進(jìn)行排序(示例中先按年齡,如果年齡一樣按姓名排序)
c.創(chuàng)建TreeSet的時(shí)候吐葱,傳入比較器對(duì)象街望;
示例代碼如下:
public class Test {
public static void main(String[] args) {
//創(chuàng)建TreeSet的時(shí)候傳入自定義的比較器
TreeSet<Student> set = new TreeSet<>(new MyComparator());
//給集合 添加元素
set.add(new Student(100, "小張"));
set.add(new Student(15, "小王"));
set.add(new Student(15, "小李"));
set.add(new Student(15, "小王"));
set.add(new Student(16, "小王"));
//增強(qiáng)for遍歷
for (Student s : set) {
System.out.println(s);
}
}
//自定義的比較器類
public static class MyComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
//1.優(yōu)先以年齡排序
//2.年齡一樣使用name排序
//先比較年齡
int a = o1.age - o2.getAge();
//System.out.println(this.age);
//System.out.println(student.getAge());
//System.out.println(a);
//次要排序條件name
//如果年齡一樣就比較姓名
if(a==0){
a = o1.name.compareTo(o2.getName());
}
// 根據(jù)a是 負(fù)整數(shù)、零或正整數(shù)弟跑,此對(duì)象是小于灾前、等于還是大于指定對(duì)象
//a是0:o1和o2一樣,不存儲(chǔ)
//a是正整數(shù):o1比o2大,在二叉樹中,o2排在o1的左邊節(jié)點(diǎn)
//a是負(fù)整數(shù):o1比o2小,在二叉樹中,o2排在o1的右邊節(jié)點(diǎn)
return a;
}
}
//自定義類
public static class Student{
int age;
String name;
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}
}
3. 兩種方式的區(qū)別
a.TreeSet構(gòu)造函數(shù)什么都不傳, 默認(rèn)按照類中Comparable的順序(沒有就報(bào)錯(cuò)ClassCastException)
b.TreeSet如果傳入Comparator, 就優(yōu)先按照Comparator
五、增強(qiáng)for概述及使用
1. 增強(qiáng)for概述孟辑,原因
增強(qiáng)for又叫foreach哎甲,是一種加強(qiáng)型的for循環(huán)操作,主要可以用于簡(jiǎn)化數(shù)組或者集合數(shù)據(jù)的輸出操作扑浸。普通for循環(huán)需要控制索引烧给,容易出現(xiàn)索引越界的問題(IndexOutOfBoundsException)燕偶。
2. 增強(qiáng)for的格式喝噪,好處,注意事項(xiàng)
1. 增強(qiáng)for循環(huán)格式如下:
for(數(shù)據(jù)類型 變量:數(shù)組|集合){
//每次循環(huán)都會(huì)自動(dòng)將數(shù)組或者集合中的內(nèi)容依次取出指么,設(shè)置給變量酝惧,可以避免索引問題
}
示例代碼:
public class Test {
public static void main(String[] args) {
//數(shù)組
int[] arr = {1, 4, 62, 431, 2};
//增強(qiáng)for遍歷輸出
for (int value :arr) {
System.out.println(value);
}
}
}
2. foreach好處
a.簡(jiǎn)化數(shù)組或者集合
b.出現(xiàn)索引越界的問題
3. 使用增強(qiáng)for注意事項(xiàng)
a.在使用增強(qiáng)型for循環(huán)不支持遍歷時(shí)刪除元素(會(huì)報(bào)異常:ConcurrentModificationException)
b.使用增強(qiáng)型for循環(huán)時(shí)榴鼎,對(duì)遍歷的集合需要做null判斷,不然可能引發(fā)空指針異常晚唇。
看如下案例:
public class Test {
public static void main(String[] args) {
//list集合
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//增強(qiáng)for遍歷輸出
for (int value :list) {
System.out.println(value);
//移除元素
list.remove(value);
}
}
}
六巫财、 了解靜態(tài)導(dǎo)入概述及使用
1. 靜態(tài)導(dǎo)入概述,注意事項(xiàng)
?如果某個(gè)類中定義的方法全部都是static類型的哩陕,那么其他類要引用此類時(shí)必須先使用import導(dǎo)入所需要的包平项,再使用“類名稱.方法()”進(jìn)行調(diào)用
示例如下:
package com.xts.im.util;
public class MyMath {
//相加
public static int add(int a,int b){
return a+b;
}
//相除
public static int div(int a,int b){
return a/b;
}
}
package com.xts.im.test;
//導(dǎo)包
import com.xts.im.util.MyMath;
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 2;
//類名稱.方法()調(diào)用
System.out.println("a+b = "+ MyMath.add(a,b));
System.out.println("a/b = "+ MyMath.div(a,b));
}
}
當(dāng)前程序MyMath兩個(gè)方法都使用了static定義,而后在不同包中的類實(shí)現(xiàn)調(diào)用悍及,如果在調(diào)用的時(shí)候不希望出現(xiàn)類名稱闽瓢,即直接在主方法中可以直接調(diào)用不同包中的static方法,那么就可以使用靜態(tài)導(dǎo)入的操作完成心赶,語法格式如下:
import static 包路徑.類名稱.*(*是通配符)
示例如下:
package com.xts.im.test;
//靜態(tài)導(dǎo)入
import static com.xts.im.util.MyMath.*;
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 2;
//使用的時(shí)候就不用再加 類名稱. 了
System.out.println("a+b = "+ add(a,b));
System.out.println("a/b = "+div(a,b));
}
}
七扣讼、了解可變參數(shù)概述及使用
1. 可變參數(shù)概述,格式
在JDK1.5之后缨叫,如果我們定義一個(gè)方法需要接受多個(gè)參數(shù)椭符,并且多個(gè)參數(shù)類型一致,我們可以對(duì)其簡(jiǎn)化成
如下格式:
修飾符 返回值類型 方法名(參數(shù)類型... 形參名){ }
其實(shí)這個(gè)書寫完全等價(jià)于:
修飾符 返回值類型 方法名(參數(shù)類型[] 形參名){ }
?只是后面這種定義耻姥,在調(diào)用時(shí)必須傳遞數(shù)組销钝,而前者可以直接傳遞數(shù)據(jù)即可。 JDK1.5以后咏闪。出現(xiàn)了簡(jiǎn)化操作曙搬。... 用在參數(shù)上,稱之為可變參數(shù)鸽嫂。出現(xiàn)了簡(jiǎn)化操作纵装。... 用在參數(shù)上,稱之為可變參數(shù)据某。
同樣是代表數(shù)組橡娄,但是在調(diào)用這個(gè)帶有可變參數(shù)的方法時(shí),不用創(chuàng)建數(shù)組(這就是簡(jiǎn)單之處)癣籽,直接將數(shù)組中的元素 作為實(shí)際參數(shù)進(jìn)行傳遞挽唉,其實(shí)編譯成的class文件,將這些元素先封裝到一個(gè)數(shù)組中筷狼,在進(jìn)行傳遞瓶籽。這些動(dòng)作都在編譯.class文件時(shí),自動(dòng)完成了埂材。
示例代碼:
public class Test {
public static void main(String[] args) {
//數(shù)組的寫法
int[] arr = {1, 4, 62, 431, 2};
int sum = getSum1(arr);
System.out.println("數(shù)組求和:"+sum);
//可變參數(shù),不用再寫數(shù)組了
int sum2 = getSum2(6, 7, 2, 12, 2121);
System.out.println("可變參數(shù)求和:"+sum2);
}
//數(shù)組寫法,所有元素的求和
public static int getSum1(int[] arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
//可變參數(shù)寫法,所有元素的求和
public static int getSum2(int... arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
}
*注意:如果在方法書寫時(shí)塑顺,這個(gè)方法擁有多參數(shù),參數(shù)中包含可變參數(shù),可變參數(shù)一定要寫在參數(shù)列表的末尾位置严拒。
八扬绪、Arrays工具類的使用
Arrays類位于 java.util 包中,主要包含了操縱數(shù)組的各種方法裤唠。
1. public static void sort(byte[] a) :對(duì)數(shù)組按照升序排序
示例代碼:
public class Test {
public static void main(String[] args) {
int[] nums = {2,5,0,4,6,-10};
//按照升序排序
Arrays.sort(nums);
for(int i :nums)
System.out.print(i+" ");
/* 之前:2 5 0 4 6 -10
* 結(jié)果:-10 0 2 4 5 6
*/
}
}
2. public static void fill(byte[] a, byte val) :可以為數(shù)組元素填充相同的值
示例代碼:
public class Test {
public static void main(String[] args) {
int[] nums = {2,5,0,4,1,-10};
Arrays.fill(nums, 1);
for(int i :nums)
System.out.print(i+" ");
/* 之前:2 5 0 4 1 -10
* 結(jié)果:1 1 1 1 1 1
*/
}
}
3. public static String toString(byte[] a):返回?cái)?shù)組的字符串形式
示例代碼:
public class Test {
public static void main(String[] args) {
int[] nums = {2,5,0,4,1,-10};
System.out.println(Arrays.toString(nums));
/*
* 結(jié)果:[2, 5, 0, 4, 1, -10]
*/
}
}
4. public static int binarySearch(byte[] a, byte key):二分法查找給定數(shù)據(jù)在數(shù)組中的索引挤牛,要求數(shù)組有序
找到的情況下:
如果key在數(shù)組中,則返回搜索值的索引种蘸。
找不到的情況下:
a.搜索值在數(shù)組范圍內(nèi)墓赴,從1開始計(jì)數(shù),得“ - 插入點(diǎn)位置”航瞭;
b.搜索值大于數(shù)組內(nèi)元素竣蹦,索引值為 – (length + 1);
c.搜索值小于數(shù)組內(nèi)元素,索引值為 – 1沧奴。
示例代碼:
public class Test {
public static void main(String[] args) {
int a[] = new int[] {1, 3, 4, 6, 8, 9};
//x1 = -4: 5不在數(shù)組中痘括,應(yīng)該插入在4和6中間,位置是從1開始滔吠,所以是4纲菌,前面加-
int x1 = Arrays.binarySearch(a, 5);
//x2 = 2,找到了疮绷,返回索引
int x2 = Arrays.binarySearch(a, 4);
//x3 = -1翰舌,沒有找到,且比最小的1還小冬骚,返回-1
int x3 = Arrays.binarySearch(a, 0);
//x4 = -7, 10不在數(shù)組中椅贱,比最大的9還大,位置是從1開始只冻,所以是7庇麦,前面加-
int x4 = Arrays.binarySearch(a, 10);
//x1 = -2: 2不在數(shù)組中,應(yīng)該插入在1和3中間喜德,位置是從1開始山橄,所以是2,前面加-
int x5 = Arrays.binarySearch(a, 2);
System.out.println("x1="+x1);
System.out.println("x2="+x2);
System.out.println("x3="+x3);
System.out.println("x4="+x4);
System.out.println("x5="+x5);
}
}
5. 特殊方法asList方法使用
//將數(shù)組/可變參數(shù)轉(zhuǎn)換為List
public static <T> List<T> asList(T... a)
示例代碼:
public class Test {
public static void main(String[] args) {
Integer[] a = new Integer[] {1, 3, 4, 6, 8, 9};
List<Integer> list = Arrays.asList(a);
System.out.println(list);
String[] str = new String[] {"a","b","c"};
List<String> strList = Arrays.asList(str);
System.out.println(strList);
}
}
*注意事項(xiàng)
a.該方法適用于對(duì)象型數(shù)據(jù)的數(shù)組(String舍悯、Integer...)
b.該方法不建議使用于基本數(shù)據(jù)類型的數(shù)組(byte,short,int,long,float,double,boolean)
c.該方法將數(shù)組與List列表鏈接起來:當(dāng)更新其一個(gè)時(shí)航棱,另一個(gè)自動(dòng)更新
d.不支持add()、remove()萌衬、clear()等方法