Java集合入門深度學習
Java集合可以分為兩大類
分別是
- Collection
- Map
兩者區別
(1) Collection是單列集合,Map是雙列集合。
(2) Collection只有Set系列要求元素唯一,Map鍵要求唯一,值可以重複。
(3) Collection資料結構針對元素,Map資料結構是針對鍵的。
泛型:
在說兩大集合體系之前先說說泛型,因為在後面的集合中都會用到; 所謂的泛型就是:型別的引數化
泛型是型別的一部分,類名+泛型是一個整體
如果有泛型,不使用時,引數的型別會自動提升成Object型別,如果再取出來的話就需要向下強轉,就可能發生型別轉化異常(ClassCaseException);不加泛型就不能在編譯期限定向集合中新增元素的型別,導致後期的處理麻煩。
加泛型和不加泛型的區別:
package 好好學java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 不加泛型,新增和遍歷
List list = new ArrayList<>();
list.add(1);
list.add("123");
list.add("hello" );
Iterator it = list.iterator();
while(it.hasNext()){
// 沒有新增泛型,這裡只能使用Object接收
Object obj = it.next();
System.out.println(obj);
}
}
}
package 好好學java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 加泛型,新增和遍歷
List<String> list = new ArrayList<String>();
list.add("123");
list.add("hello");
Iterator<String> it = list.iterator();
while(it.hasNext()){
// 因為添加了泛型,就說明集合中裝的全部都是String型別的資料
// 所以這裡用String型別接收,就不會發生異常,並且可以使用String的方法
String str = it.next();
System.out.println(str.length());
}
}
}
自定義帶泛型的類:
package 好好學java;
public class Test {
// 自定義一個帶有一個引數的泛型類,可以向傳入什麼型別就傳入什麼型別
public static void main(String[] args) {
// 進行測試, 傳入一個String物件
Person<String> perStr = new Person<String>();
perStr.setT("我是字串");
String str = perStr.getT();
System.out.println(str);
// 進行測試,傳入一個Integer物件
Person<Integer> perInt = new Person<Integer>();
perInt.setT(100);
Integer intVal = perInt.getT();
System.out.println(intVal);
}
}
//自定義一個帶有一個引數的泛型類
class Person<T>{
private T t;
void setT(T t){
this.t = t;
}
T getT(){
return t;
}
}
實現帶有泛型的介面型別:
實現介面的同時, 指定了介面中的泛型型別. (定義類時確定);
public class GenericImpl1 implements GenericInter<String> {}
實現介面時, 沒有指定介面中的泛型型別.此時, 需要將該介面的實現類定義為泛型類.介面的型別需要在建立實現類物件時才能真正確定其型別. (始終不確定型別, 直到建立物件時確定型別);
public class GenericImpl2<T> implements GenericInter<T> {}
泛型的萬用字元(?):
上限限定:比如定義方法的時候出現\
public void getFunc(List<? extends Animal> an)
那麼表示這裡的引數可以傳入Animal,或者 Animal的子類
下限限定: 比如定義方法的時候出現,
public void getFunc(Set<? super Animal> an ),
那麼表示這裡的引數可以傳入Animal,或者Animal的父類
使用泛型的注意點:
1、泛型不支援基本資料型別
2、泛型不支援繼承,必須保持前後一致(比如這樣是錯誤的:
:List<Object> list = new ArrayList<String>();
)
Collection體系:
ollection包括兩大體系,List和Set
List的特點:
存取有序,有索引,可以根據索引來進行取值,元素可以重複
Set的特點:
存取無序,元素不可以重複
List:
下面有ArrayList,LinkedList,Vector(已過時)
集合的的最大目的就是為了存取;List集合的特點就是存取有序,可以儲存重複的元素,可以用下標進行元素的操作
ArrayList: 底層是使用陣列實現,所以查詢速度快,增刪速度慢。
package 好好學java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
// 使用ArrayList進行新增和遍歷
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("介面1");
list.add("介面2");
list.add("介面3");
// 第一種遍歷方式,使用迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
String next = it.next();
System.out.println(next);
}
System.out.println("-------------------");
// 第二種遍歷方式,使用foreach
for (String str : list){
System.err.println(str);
}
}
}
LinkedList:是基於連結串列結構實現的,所以查詢速度慢,增刪速度快,提供了特殊的方法,對頭尾的元素操作(進行增刪查)。
使用LinkedList來實現棧和佇列;棧是先進後出,而佇列是先進先出。
package com.xiaoshitou.classtest;
import java.util.LinkedList;
/**
* 利用LinkedList來模擬棧
* 棧的特點:先進後出
* @author Beck
*
*/
public class MyStack {
private LinkedList<String> linkList = new LinkedList<String>();
// 壓棧
public void push(String str){
linkList.addFirst(str);
}
// 出棧
public String pop(){
return linkList.removeFirst();
}
// 檢視
public String peek(){
return linkList.peek();
}
// 判斷是否為空
public boolean isEmpty(){
return linkList.isEmpty();
}
}
package 好好學java;
public class Test {
public static void main(String[] args) {
// 測試棧
StackTest stack = new StackTest();
stack.push("我是第1個進去的");
stack.push("我是第2個進去的");
stack.push("我是第3個進去的");
stack.push("我是第4個進去的");
stack.push("我是第5個進去的");
// 取出
while (!stack.isEmpty()){
String pop = stack.pop();
System.out.println(pop);
}
// 列印結果
/*我是第5個進去的
我是第4個進去的
我是第3個進去的
我是第2個進去的
我是第1個進去的*/
}
}
LinkedList實現Queue:
package 好好學java;
import java.util.LinkedList;
/**
* 利用linkedList來實現佇列
* 佇列: 先進先出
* @author Beck
*
*/
public class QueueTest {
private LinkedList<String> link = new LinkedList<String>();
// 放入
public void put(String str){
link.addFirst(str);
}
// 獲取
public String get(){
return link.removeLast();
}
// 判斷是否為空
public boolean isEmpty(){
return link.isEmpty();
}
}
package 好好學java;
public class Test {
public static void main(String[] args) {
// 測試佇列
QueueTest queue = new QueueTest();
queue.put("我是第1個進入佇列的");
queue.put("我是第2個進入佇列的");
queue.put("我是第3個進入佇列的");
queue.put("我是第4個進入佇列的");
// 遍歷佇列
while (!queue.isEmpty()){
String str = queue.get();
System.out.println(str);
}
// 列印結果
/*我是第1個進入佇列的
我是第2個進入佇列的
我是第3個進入佇列的
我是第4個進入佇列的*/
}
}
Vector:因為已經過時,被ArrayList取代了;它還有一種迭代器通過vector.elements()獲取,判斷是否有元素和取元素的方法為:hasMoreElements(),nextElement()。
package 好好學java;
import java.util.Enumeration;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
Vector<String> vector = new Vector<String>();
vector.add("搜尋");
vector.add("vector");
vector.add("list");
Enumeration<String> elements = vector.elements();
while (elements.hasMoreElements()){
String nextElement = elements.nextElement();
System.out.println(nextElement);
}
}
}
Set:
Set集合的特點:元素不重複,存取無序,無下標 Set集合下面有:HashSet,LinkedHashSet,TreeSet
HashSet儲存字串:
package 好好學java;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 利用HashSet來存取
Set<String> set = new HashSet<String>();
set.add("我的天");
set.add("我是重複的");
set.add("我是重複的");
set.add("welcome");
// 遍歷 第一種方式 迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
System.out.println("--------------");
for (String str : set){
System.out.println(str);
}
// 列印結果,重複的已經去掉了
/*我的天
welcome
我是重複的
--------------
我的天
welcome
我是重複的*/
}
那雜湊表是怎麼來保證元素的唯一性的呢,雜湊表是通過hashCode和equals方法來共同保證的。
雜湊表的儲存資料過程(雜湊表底層也維護了一個數組):
根據儲存的元素計算出hashCode值,然後根據計算得出的hashCode值和陣列的長度進行計算出儲存的下標;如果下標的位置無元素,那麼直接儲存;如果有元素,那麼使用要存入的元素和該元素進行equals方法,如果結果為真,則已經有相同的元素了,所以直接不存;如果結果假,那麼進行儲存,以連結串列的形式儲存。
演示HashSet來儲存自定義物件:
package 好好學java;
public class Person {
// 屬性
private String name;
private int age;
// 構造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 要讓雜湊表儲存不重複的元素,就必須重寫hasCode和equals方法
@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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// getter & setter
...
}
package 好好學java;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 利用HashSet來存取自定義物件 Person
Set<Person> set = new HashSet<Person>();
set.add(new Person("cy", 12));
set.add(new Person("李四", 13));
set.add(new Person("王五", 22));
set.add(new Person("cy", 12));
// 遍歷
for (Person p : set){
System.out.println(p);
}
// 結果:向集合中儲存兩個cy物件,但是集合中就成功儲存了一個
/*Person [name=王五, age=22]
Person [name=李四, age=13]
Person [name=cy, age=12]*/
}
}
所以在向HashSet集合中儲存自定義物件時,為了保證set集合的唯一性,那麼必須重寫hashCode和equals方法。
LinkedHashSet:
是基於連結串列和雜湊表共同實現的,所以具有存取有序,元素唯一
package 好好學java;
import java.util.LinkedHashSet;
public class Test {
public static void main(String[] args) {
// 利用LinkedHashSet來存取自定義物件 Person
LinkedHashSet<Person> set = new LinkedHashSet<Person>();
set.add(new Person("cy", 12));
set.add(new Person("李四", 13));
set.add(new Person("王五", 22));
set.add(new Person("cy", 12));
// 遍歷
for (Person p : set){
System.out.println(p);
}
// 結果:向集合中儲存兩個cy物件,但是集合中就成功儲存了一個,
// 並且存進的順序,和取出來的順序是一致的
/*Person [name=cy, age=12]
Person [name=李四, age=13]
Person [name=王五, age=22]*/
}
}
TreeSet:
特點:存取無序,元素唯一,可以進行排序(排序是在新增的時候進行排序)。
TreeSet是基於二叉樹的資料結構,二叉樹的:一個節點下不能多餘兩個節點。
二叉樹的儲存過程:
如果是第一個元素,那麼直接存入,作為根節點,下一個元素進來是會跟節點比較,如果大於節點放右邊的,小於節點放左邊;等於節點就不儲存。後面的元素進來會依次比較,直到有位置儲存為止
TreeSet集合儲存String物件
package 好好學java;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("abc");
treeSet.add("zbc");
treeSet.add("cbc");
treeSet.add("xbc");
for (String str : treeSet){
System.out.println(str);
}
// 結果:取出來的結果是經過排序的
/*
abc
cbc
xbc
zbc*/
}
}
TreeSet保證元素的唯一性是有兩種方式:
1、自定義物件實現Comparable介面,重寫comparaTo方法,該方法返回0表示相等,小於0表示準備存入的元素比被比較的元素小,否則大於0;
2、在建立TreeSet的時候向構造器中傳入比較器Comparator介面實現類物件,實現Comparator介面重寫compara方法。
如果向TreeSet存入自定義物件時,自定義類沒有實現Comparable介面,或者沒有傳入Comparator比較器時,會出現ClassCastException異常
下面就是演示用兩種方式來儲存自定義物件
package 好好學java;
public class Person implements Comparable<Person>{
// 屬性
private String name;
private int age;
// 構造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 要讓雜湊表儲存不重複的元素,就必須重寫hasCode和equals方法
@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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// getter & setter
...
@Override
public int compareTo(Person o) {
int result = this.age - o.age;
if (result == 0){
return this.name.compareTo(o.name);
}
return result;
}
}
package 好好學java;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
// 利用TreeSet來儲存自定義類Person物件
TreeSet<Person> treeSet = new TreeSet<Person>();
// Person類實現了Comparable介面,並且重寫comparaTo方法
// 比較規則是先按照 年齡排序,年齡相等的情況按照年齡排序
treeSet.add(new Person("張山1", 20));
treeSet.add(new Person("張山2", 16));
treeSet.add(new Person("張山3", 13));
treeSet.add(new Person("張山4", 17));
treeSet.add(new Person("張山5", 20));
for (Person p : treeSet){
System.out.println(p);
}
// 結果:按照comparaTo方法內的邏輯來排序的
/*
Person [name=張山3, age=13]
Person [name=張山2, age=16]
Person [name=張山4, age=17]
Person [name=張山1, age=20]
Person [name=張山5, age=20]
*/
}
}
另一種方式:使用比較器Comparator
package 好好學java;
public class Person{
// 屬性
private String name;
private int age;
// 構造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 要讓雜湊表儲存不重複的元素,就必須重寫hasCode和equals方法
@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;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// getter & setter
...
}
package 好好學java;
import java.util.Comparator;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
// 利用TreeSet來儲存自定義類Person物件
// 建立TreeSet物件的時候傳入Comparator比較器,使用匿名內部類的方式
// 比較規則是先按照 年齡排序,年齡相等的情況按照年齡排序
TreeSet<Person> treeSet = new TreeSet<Person>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if (o1 == o2){
return 0;
}
int result = o1.getAge() - o2.getAge();
if (result == 0){
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
treeSet.add(new Person("張山1", 20));
treeSet.add(new Person("張山2", 16));
treeSet.add(new Person("張山3", 13));
treeSet.add(new Person("張山4", 17));
treeSet.add(new Person("張山5", 20));
for (Person p : treeSet){
System.out.println(p);
}
// 結果:按照compara方法內的邏輯來排序的
/*
Person [name=張山3, age=13]
Person [name=張山2, age=16]
Person [name=張山4, age=17]
Person [name=張山1, age=20]
Person [name=張山5, age=20]
*/
}
}
比較器總結:
Collection體系總結:
List : “特點 :” 存取有序,元素有索引,元素可以重複.
ArrayList : 陣列結構,查詢快,增刪慢,執行緒不安全,因此效率高.
Vector : 陣列結構,查詢快,增刪慢,執行緒安全,因此效率低.
LinkedList : 連結串列結構,查詢慢,增刪快,執行緒不安全,因此效率高.
addFirst() removeFirst() getFirst()
Set :”特點 :” 存取無序,元素無索引,元素不可以重複.
HashSet : 儲存無序,元素無索引,元素不可以重複.底層是雜湊表.
請問 : 雜湊表如何保證元素唯一呢 ? 底層是依賴 hashCode 和 equals 方法.
當儲存元素的時候,先根據 hashCode + 陣列長度 計算出一個索引,判斷索引位置是否有元素.
如果沒有元素,直接儲存,如果有元素,先判斷 equals 方法,比較兩個元素是否相同,不同則儲存,相同則捨棄.
我們自定義物件儲存的元素一定要實現 hashCode 和 equals.
LinkedHashSet : 儲存有序,元素不可以重複.
TreeSet : 存取無序, 但是可以排序 (自然排序), 元素不可以重複.
有兩種排序方式 :
自然排序 :
我們的元素必須實現 Comparable 介面.可比較的.實現 CompareTo 方法.
比較器排序 :
我們需要自定義類,實現Comparetor介面,這個類就是比較器實現 compare 方法.
然後在建立 TreeSet 的時候,把比較器物件作為引數傳遞給 TreeSet.
Map:
Map是一個雙列集合,其中儲存的是鍵值對,鍵要求保持唯一性,值可以重複
鍵值是一一對應的,一個鍵只能對應一個值
Map的特點:是存取無序,鍵不可重複
Map在儲存的時候,將鍵值傳入Entry,然後儲存Entry物件
其中下面有HashMap,LinkedHashMap和TreeMap
HashMap:
是基於雜湊表結構實現的,所以儲存自定義物件作為鍵時,必須重寫hasCode和equals方法。存取無序的
下面演示HashMap以自定義物件作為鍵:
package 好好學java;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 利用HashMap儲存,自定義物件Person作為鍵
// 為了保證鍵的唯一性,必須重寫hashCode和equals方法
HashMap<Person,String> map = new HashMap<Person,String>();
map.put(new Person("cy", 12), "JAVA");
map.put(new Person("李四", 13), "IOS");
map.put(new Person("小花", 22), "JS");
map.put(new Person("sihai", 32), "PHP");
map.put(new Person("cy", 12), "C++");
Set<Entry<Person, String>> entrySet = map.entrySet();
Iterator<Entry<Person, String>> it = entrySet.iterator();
while (it.hasNext()){
Entry<Person, String> entry = it.next();
System.out.println(entry.getKey() + "---" + entry.getValue());
}
// 結果:存入的時候添加了兩個cy,如果Map中鍵相同的時候,當後面的值會覆蓋掉前面的值
/*
Person [name=李四, age=13]---IOS
Person [name=cy, age=12]---C++
Person [name=sihai, age=32]---PHP
Person [name=小花, age=22]---JS
*/
}
}
LinkedHashMap:
用法跟HashMap基本一致,它是基於連結串列和雜湊表結構的所以具有存取有序,鍵不重複的特性
下面演示利用LinkedHashMap儲存,注意存的順序和遍歷出來的順序是一致的:
package 好好學java;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class Test {
public static void main(String[] args) {
// 利用LinkedHashMap儲存,自定義物件Person作為鍵
// 為了保證鍵的唯一性,必須重寫hashCode和equals方法
LinkedHashMap<Person,String> map = new LinkedHashMap<Person,String>();
map.put(new Person("cy", 12), "JAVA");
map.put(new Person("李四", 13), "IOS");
map.put(new Person("小花", 22), "JS");
map.put(new Person("sihai", 32), "PHP");
map.put(new Person("cy", 12), "C++");
// foreach遍歷
for (Entry<Person,String> entry : map.entrySet()){
System.out.println(entry.getKey()+"==="+entry.getValue());
}
// 結果:存入的時候添加了兩個cy,如果Map中鍵相同的時候,當後面的值會覆蓋掉前面的值
// 注意:LinkedHashMap的特點就是存取有序,取出來的順序就是和存入的順序保持一致
/*
Person [name=cy, age=12]===C++
Person [name=李四, age=13]===IOS
Person [name=小花, age=22]===JS
Person [name=sihai, age=32]===PHP
*/
}
}
TreeMap:
給TreeMap集合中儲存自定義物件,自定義物件作為TreeMap集合的key值。由於TreeMap底層使用的二叉樹,其中存放進去的所有資料都需要排序,要排序,就要求物件具備比較功能。物件所屬的類需要實現Comparable介面。或者給TreeMap集合傳遞一個Comparator介面物件。
利用TreeMap存入自定義物件作為鍵:
package 好好學java;
import java.util.Comparator;
import java.util.Map.Entry;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
// 利用TreeMap儲存,自定義物件Person作為鍵
// 自定義物件實現Comparable介面或者傳入Comparator比較器
TreeMap<Person,String> map = new TreeMap<Person,String>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if (o1 == o2){
return 0;
}
int result = o1.getAge() - o2.getAge();
if (result == 0){
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
map.put(new Person("cy", 12), "JAVA");
map.put(new Person("李四", 50), "IOS");
map.put(new Person("小花", 32), "JS");
map.put(new Person("sihai", 32), "PHP");
map.put(new Person("cy", 12), "C++");
// foreach遍歷
for (Entry<Person,String> entry : map.entrySet()){
System.out.println(entry.getKey()+"==="+entry.getValue());
}
// 結果:存入的時候添加了兩個cy,如果Map中鍵相同的時候,當後面的值會覆蓋掉前面的值
// 注意:TreeMap 取出來的順序是經過排序的,是根據compara方法排序的
/*
Person [name=cy, age=12]===C++
Person [name=小花, age=32]===JS
Person [name=sihai, age=32]===PHP
Person [name=李四, age=50]===IOS
*/
}
}
文章有不當之處,歡迎指正,你也可以關注我的微信公眾號:好好學java,獲取優質學習資源,也可以加入QQ技術交流群:766946816,咋們來聊聊java。
作者原版