Java類集框架詳細匯總-底層分析
前言:
Java的類集框架比較多,也十分重要,在這裡給出圖解,可以理解為相應的繼承關係,也可以當作重要知識點回顧;
Collection集合接口
繼承自:Iterable
public interface Collection<E> extends Iterable<E>
java.util.Collection
是單值集合操作的最大父接口,其中有幾個核心操作方法以及常用操作方法;
Modifier and Type | Method(public) | Description |
---|---|---|
boolean |
add(E e) |
確保此集合包含指定的元素(可選操作)。 |
boolean |
addAll(Collection<? extends E> c) |
將指定集合中的所有元素添加到此集合(可選操作)。 |
void |
clear() |
從集合中刪除所有元素(可選操作)。 |
boolean |
contains(Object o) |
如果該集合包含指定的元素,則返回true。 |
boolean |
remove(Object o) |
如果存在,則從此集合中刪除指定元素的單個實例(可選操作)。 |
int |
size() |
返回此集合中的元素數。 |
Object[] |
toArray() |
返回一個包含此集合中所有元素的數組。 |
Iterator<E> |
iterator() |
返回對此集合中的元素進行迭代的迭代器。 |
上面方法中有兩個特殊的方法就是cotains
與remove
;都需要equals
方法的支持才能刪除與查詢數據;否則找不到元素。
後面都是衍生出的子類方法。
List集合
最大特點:允許保存重複的元素,並在其父接口上擴充了其他的方法;
繼承關係:
public interface List<E> extends Collection<E>
Modifier and Type | Method(public) | Description |
---|---|---|
void |
add(int index, E element) |
Inserts the specified element at the specified position in this list (optional operation). |
boolean |
add(E e) |
Appends the specified element to the end of this list (optional operation). |
ListIterator<E> |
listIterator() |
Returns a list iterator over the elements in this list (in proper sequence). |
static <E> List<E> |
of() |
Returns an unmodifiable list containing zero elements. |
default void |
forEach(Consumer<? super T> action) |
Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception. |
實例:
package Java從入門到項目實戰.Java類集框架.List集合;
import java.util.List;
public class 多數據保存 {
public static void main(String[] args) {
List<String> all = List.of("xbhg","Hello","World","welcome");
Object[] result = all.toArray();
for (Object t: result) {
System.out.println(t);
}
System.out.println("----------分割線----------");
all.forEach(System.out::println); //方法引用部分的引用構造方法
}
}
ArrayList子類
繼承結構如下:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
實例化:重複元素允許保存並且按照添加時的順序保存;
package Java從入門到項目實戰.Java類集框架.List集合;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayList實例化List {
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
all.add("Hello");
all.add("你好");
all.add("你好");
all.add("xbhog");
System.out.println(all);
all.forEach(System.out::print);
//lambda表達式
all.forEach((str)->{
System.out.print(str+"、");
});
}
}
集合操作方法:
package Java從入門到項目實戰.Java類集框架.List集合;
import java.util.ArrayList;
import java.util.List;
public class ArrayLIst集合相關操作 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
System.out.println("集合是否為空?"+all.isEmpty()+"、集合元素個數"+all.size());
all.add("1");
all.add("2");
all.add("3");
System.out.println("集合是否為空?"+all.isEmpty()+"、集合元素個數"+all.size());
System.out.println(all.get(1));
System.out.println(all.remove("3"));
System.out.println("集合是否為空?"+all.isEmpty()+"、集合元素個數"+all.size());
}
}
ArrayList原理分析:重點
首先需要明確ArrayList是通過數組實現的;這樣就出現了ArrayList通過什麼樣的方式進行的擴容操作,以及在什麼情況下才會擴容?
ArrayList類中的數組是在構造方法中進行的空間開闢的;其對應的有無參和有參構造方法:
無參構造方法:使用空數組(長度為0)初始化,在第一次使用時會為其開闢空間為(初始化程度為10);
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//默認的開闢空間大小
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
有參構造方法:長度大於0則以指定長度開闢數組空間;如果長度為0,則按照無參構造方法進行;如果負數則拋出ILLegaLArgumentException異常;
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. //bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
當數組擴充後利用數組複製的形式,將舊數組中的數據複製到開闢的新數組中;
其最大程度為:
/**
* The maximum size of array to allocate (unless necessary).
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList保存自定義類對象:
該操作必然包含了相關的增刪改查;由於contains與remove方法的實現都需要通過對象比較倆完成;所以我們需要覆寫equals方法
package Java從入門到項目實戰.Java類集框架.List集合;
import java.util.ArrayList;
import java.util.List;
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 int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public boolean equals(Object obj){
if(this== obj) return true;
if(obj == null) return false;
if(!(obj instanceof Person)) return false;
Person pe = (Person) obj;
return this.name.equals(pe.name) && this.age == pe.age;
}
@Override
public String toString() {
return "姓名:"+this.name +"、年齡:"+this.age;
}
}
public class ArrayList保存自定義類對象 {
public static void main(String[] args) {
List<Person> std = new ArrayList<Person>();
std.add(new Person("xbhog",1));
std.add(new Person("小明",2));
std.add(new Person("小白",3));
System.out.println("-------刪除前的數據內容----------");
std.forEach((person)->{
System.out.println("姓名:"+person.getName()+"、年齡:"+person.getAge());
});
System.out.println("-------刪除後的數據內容----------");
std.remove(new Person("小白",3));
std.forEach((person)->{
System.out.println("姓名:"+person.getName()+"、年齡:"+person.getAge());
});
System.out.println("-------查看數據是否存在----------");
System.out.println(std.contains(new Person("小明",2)));
}
}
LinkedList子類:
繼承結構如下:基於鏈表形式的實現
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
實現LinkedList的集合操作:
package Java從入門到項目實戰.Java類集框架.List集合;
import java.util.LinkedList;
import java.util.List;
public class LinkList鏈表操作 {
public static void main(String[] args) {
List<String> all = new LinkedList<String>();
all.add("java");
all.add("python");
all.add("Linux");
System.out.println(all);
System.out.println(all.get(2));
System.out.println(all.get(1));
}
}
Vector子類:
繼承結構如下:
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
從繼承結構上可以看出,Vector子類的使用方式與ArrayList的使用方式相同;
package Java從入門到項目實戰.Java類集框架.List集合;
import java.util.List;
import java.util.Vector;
public class Vector子類實現List接口 {
public static void main(String[] args) {
List<String> all = new Vector<String>();
all.add("asda");
all.add("你好");
all.add("buhao");
System.out.println(all);
}
}
不同點:
以下是vector操作方法,採用的方式是synchronized 同步處理;屬於線程安全,但是效率沒有ArrayList高;
在考慮線程並發訪問的情況下才能去使用vector子類。
public synchronized void copyInto(Object[] anArray)
Set集合
主要特點是:內部不允許保存重複元素
繼承結構如下:
public interface Set<E> extends Collection<E>
實例化:
package Java從入門到項目實戰.Java類集框架.Set集合;
import java.util.Set;
public class set的基本使用 {
public static void main(String[] args) {
//不能有重複值,如果有的話會報錯
//Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: 世界
// Set<String> all = Set.of("你好","xbhog","世界","世界");
Set<String> all = Set.of("你好","xbhog","世界");
System.out.println(all);
}
}
HashSet子類:
特點:散列存放且不允許保存重複元素 即:無序存放
繼承結構如下:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
HashSet保存數據:
package Java從入門到項目實戰.Java類集框架.Set集合;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSet保存數據 { //數據採用無序的保存方式,且不允許保存重複的數據
public static void main(String[] args) {
//保存的類型是String
Set<String> all = new HashSet<String>();
all.add("小李");
all.add("小紅");
all.add("小1");
all.add("小2");
all.add("小花");
all.add("小花");
System.out.println(all);
Iterator<String> iter = all.iterator();
while(iter.hasNext()){
String str = iter.next();
System.out.print(str+"、");
}
}
}
LinkedHashSet子類:JDK1.4加入—解決HashSet無法順序保存的數據
實現是基於鏈表保存的數據:增加的順序就是集合的保存順序,且不會保存重複的數據。
package Java從入門到項目實戰.Java類集框架.Set集合;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
public class 鏈表實現LinkHashSet { //基於鏈表的操作,且保存的數據為順序保存
public static void main(String[] args) {
Set<String> all = new LinkedHashSet<String>();
all.add("小李老師");
all.add("小bai");
all.add("小明");
all.add("小黃");
System.out.println(all);
Iterator<String> iter = all.iterator();
while (iter.hasNext()) {
String str = iter.next();
System.out.println(str + "、");
}
}
}
TreeSet子類:
特點:使得集合中保存的數據進行有序排列
其繼承結構如下:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
TreeSet子類繼承AbstractSet抽象類並實現了NavigableSet接口(該接口為排序標準接口,是Set的子類)
TreeSet保存數據:
package Java從入門到項目實戰.Java類集框架.Set集合;
import java.util.*;
public class TreeSet子類的有序排列 {
//所有保存的數據按照從大到小的順序(字符按照字母大小順序依次比較)
public static void main(String[] args) {
Set<String> all = new TreeSet<String>();
all.add("11");
all.add("hello");
all.add("hello");
all.add("world");
all.add("add");
all.add("部署");
all.add("啊哈");
System.out.println(all);
}
}
優先級:數字排序>字母排序>漢字排序
TreeSet子類排序分析:
該類子在進行有序數據存儲時依據的是Comparable接口實現排序;需要注意的是在覆寫compareTo()方法時需要進行類中全部屬性的比較;否則出現部分屬性相同時被誤判為同一個對象;導致重複元素判斷失敗;
package Java從入門到項目實戰.Java類集框架.Set集合;
import java.util.Set;
import java.util.TreeSet;
class Member implements Comparable<Member>{
private String name;
private int age;
public Member(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "姓名:"+this.name +"、年齡:"+this.age;
}
@Override
public int compareTo(Member 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 TreeSet子類排序分析 {
public static void main(String[] args) {
Set<Member> all = new TreeSet<Member>();
all.add(new Member("張三",12));
all.add(new Member("李四",12));
all.add(new Member("王五",20));
all.add(new Member("王五",20));
all.forEach(System.out::println);
}
}
關於compareTo的相關描述,可以到源碼下的注釋中翻譯了解。
重複元素消除:(非排序的集合中的重複元素)
依靠兩種方法:
- Hash碼:public int Hashcode();
- 對象比較:public boolean equals(Object obj);
在進行對象比較的過程中,首先會使用hashCode()方法與集合中已經保存的代碼進行匹配比較;如果代碼相同則再使用equals()方法進行屬性的依次比較;如果全部相同;則為相同元素;
package Java從入門到項目實戰.Java類集框架.Set集合;
import java.util.*;
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 int getAge() {
return age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
if (age != person.age) return false;
if (name != null ? !name.equals(person.name) : person.name != null) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
public class 重複元素消除 {
public static void main(String[] args) {
Set<Person> all = new HashSet<Person>();
all.add(new Person("張三",19));
all.add(new Person("李四",19));
all.add(new Person("王五",20));
all.add(new Person("王五",20));
all.add(new Person("魏六",66));
all.add(new Person("xbhog",10));
System.out.println("------------第一種輸入方式-----------");
all.forEach((person -> {
System.out.println(person.getName()+"----"+person.getAge());
}));
System.out.println("------------第二種輸入方式-----------");
Iterator<Person> iter = all.iterator();
while(iter.hasNext()){
Person per = iter.next();
System.out.println(per.getName() + " "+per.getAge());
}
}
}
通過hashSet 保存了重複元素,再兩個方法的作用實現去重操作。
集合輸出
在類框架中的對於集合的標準輸出為:Iterator
、ListIterator
、Enumeration
、foreach
;
Iterator迭代輸出:
迭代輸出:依次判斷每個元素,判斷其是否有內容,如果有內容就輸出。
Iterator接口依靠Iterable接口中的iterate()方法實例化的;
Iterator常用方法:
boolean |
hasNext() |
Returns true if the iteration has more elements. |
---|---|---|
E |
next() |
返回迭代中的下一個元素。 |
default void |
remove() |
從基礎集合中移除該迭代器返回的最後一個元素(可選操作)。 |
boolean |
hasNext() |
如果迭代有更多元素則返回true。 |
實例:
package Java從入門到項目實戰.Java類集框架.集合輸出;
import java.util.Iterator;
import java.util.Set;
public class Iterator輸出Set集合 {
public static void main(String[] args) {
Set<String> all = Set.of("Hello","World","xbhog");
Iterator<String> iter = all.iterator();
while(iter.hasNext()){
String str = iter.next();
System.out.print(str+"、");
}
}
}
關於數據刪除的問題:
在Collection中與Iterator都有remove方法;那麼應該選擇什麼呢:
Collection不管三七二十一,就給你刪除了,這樣會造成Java.util.ConcurrentModificationException錯誤;
而Iterator在迭代的時候;都會需要依據存儲的數據內容進行判斷;
所以只有Iterator接口中的remove才是實現刪除數據的正確方法。
如:
package cn.mldn.demo;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class JavaCollectDemo {
public static void main(String[] args) {
// 如果使用Set.of()或List.of()創建的集合不支持刪除操作
Set<String> all = new HashSet<String>();
all.add("小白"); // 保存數據
all.add("Java"); // 保存數據
all.add("Java"); // 保存重複數據
all.add("xbhog"); // 保存數據
Iterator<String> iter = all.iterator();
while (iter.hasNext()) { // 集合是否有數據
String str = iter.next(); // 獲取數據
if ("Java".equals(str)) {
iter.remove() ; // 刪除當前數據
} else {
System.out.print(str + "、");
}
}
}
}
ListIterator雙向迭代:
首先區別Iterator的作用:
Iterator完成的是從前向後單向輸出
ListIterator完成的是從後向前輸出
但是只有實現Iterator從前向後的輸出後才能實現ListIterator從後向前的輸出(注意先後順序);
因為只有實現從前向後輸出結束後,指針才執行最後;
如果順序相反則會輸出為空。
其下的擴充方法:
Modifier and Type | Method | Description |
---|---|---|
boolean |
hasPrevious() |
如果該列表迭代器在反向遍歷列表時擁有更多元素,則返回true。 |
E |
previous() |
返回列表中的前一個元素,並向後移動光標位置。 |
實例:執行雙向迭代
package Java從入門到項目實戰.Java類集框架.集合輸出;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class 雙向迭代輸出ListIterator {
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
all.add("小李");
all.add("小宋");
all.add("xbhog");
ListIterator<String> iter = all.listIterator();
System.out.println("執行從後往前的操作的話必須需要先執行從前往後的操作,這樣才能是指針指向後面");
//如果不按照相關操作進行,則從後往前的操作輸出為空;
System.out.println("從前往後輸出:-----------");
while(iter.hasNext()){
System.out.println(iter.next());
}
System.out.println("從後往前輸出:------------");
while(iter.hasPrevious()){ //判斷是否有前一個元素
System.out.println(iter.previous()); //有的輸出前一個元素
}
}
}
ListIterator接口實現了List集合的雙向迭代操作。
Enumeration枚舉輸出:
該類型的輸出是建立在Vector集合上的;相當於依附產品;
其接口常用方法:
Modifier and Type | Method | Description |
---|---|---|
boolean |
hasMoreElements() |
測試此枚舉是否包含更多元素。 |
E |
nextElement() |
如果該枚舉對象至少還有一個要提供的元素,則返回該枚舉的下一個元素。 |
實例:輸出vector集合數據
package cn.mldn.demo;
import java.util.Enumeration;
import java.util.Vector;
public class JavaCollectDemo {
public static void main(String[] args) {
Vector<String> all = new Vector<String>(); // 實例化Vector
all.add("小黃"); // 保存數據
all.add("Java"); // 保存數據
all.add("xbhog"); // 保存數據
Enumeration<String> enu = all.elements() ; // 獲取Enumeration實例
while (enu.hasMoreElements()) {
String str = enu.nextElement() ;
System.out.print(str + "、");
}
}
}
注意Enumeration只有輸出操作沒有刪除操作。
foreach輸出:
沒啥好說的,既可以實現數組輸出外,也支持集合的輸出;
package cn.mldn.demo;
import java.util.HashSet;
import java.util.Set;
public class JavaCollectDemo {
public static void main(String[] args) {
Set<String> all = new HashSet<String>(); // 實例化Set
all.add("小黃"); // 保存數據
all.add("Java"); // 保存數據
all.add("xbhog"); // 保存數據
for (String str : all) {
System.out.print(str + "、");
}
}
}
實現自定義foreach輸出:
首先需要知道實現foreach需要Iterator接口的支持;所以在Set與List集合才可以通過foreach實現輸出;
如果我們自己要實現自定義類的輸出,那麼我們就需要實例化Iterable接口完成iterator的功能;
package cn.mldn.demo;
import java.util.Iterator;
class Message implements Iterable<String> {// 支持foreach輸出
private String[] content = { "Java", "Python", "Ubuntu" }; // 信息內容
private int foot; // 操作腳標
@Override
public Iterator<String> iterator() { // 獲取Iterator實例
return new MessageIterator();
}
private class MessageIterator implements Iterator<String> {
@Override
public boolean hasNext() { // 判斷是否存在內容
return Message.this.foot <
Message.this.content.length;
}
@Override
public String next() { // 獲取數據
return Message.this.content[Message.this.foot++];
}
}
}
public class JavaCollectDemo {
public static void main(String[] args) {
Message message = new Message(); // Iterable接口實例
for (String msg : message) { // foreach輸出
System.out.print(msg + "、");
}
}
}
Map集合
map的集合形式是鍵值對的方式;
其常用方法:
Modifier and Type | Method | Description |
---|---|---|
V |
get(Object key) |
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. |
V |
put(K key, V value) |
Associates the specified value with the specified key in this map (optional operation). |
static <K,V> Map<K,V> |
of() |
Returns an unmodifiable map containing zero mappings. |
其中Map.of()可以將每一組數據轉為map進行保存;
使用Map保存Key-Value數據:
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class 保存Key_Value數據 {
public static void main(String[] args) {
// 如果Key重複 則會報錯:java.lang.IllegalArgumentException
// 如果Value與Key設置為null,則會報錯:java.lang.NullPointerException
Map<String,Integer> map = Map.of("one",1,"two",null);
System.out.println(map);
// System.out.println(map.get("one"));
// Put只能在map子類中使用
}
}
注意點:
- 如果Key重複 則會報錯:java.lang.IllegalArgumentException
- 如果Value與Key設置為null,則會報錯:java.lang.NullPointerException
HashMap子類:
特點:採用散列方式保存數據,即無序排列
繼承結構:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
HashMap進行map集合的操作:
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
public class HahMap子類 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
// 進行Map的集合操作
// map.put("One",1);
// map.put("Two",2);
// map.put("Three",3);
// System.out.println(map);
// Map數據的保存方法:
System.out.println(map.put("One",1)); //保存數據,但是保存的數據的key不存在,返回null
System.out.println(map.put("One",101)); // 返回1,也就是返回的是覆蓋掉的值
map.put("Three",3);
map.put("demo1",4);
map.put("demo2",null);
map.put("Te",6);
map.put(null,7);
/*結果
* null、7
* */
System.out.println(map.get("demo2"));
System.out.println(map.get(null));
}
}
注意兩個輸出的注釋:
//Map數據的保存方法:
System.out.println(map.put("One",1)); //保存數據,但是保存的數據的key不存在,返回null
System.out.println(map.put("One",101)); // 返回1,也就是返回的是覆蓋掉的值
在使用Map保存數據的時候Key與Value都不能使用null,但是使用HashMap進行保存數據可以將Key或Value設置為null,當然也可以Key=Value=null,但是這樣的實現保存毫無意義。
put方法在發生覆蓋錢都可以返回原始內容,這樣就可以依據返回結果來判斷所設置的key是否存在;
HashMap數據擴充操作原理分析:
1) 首先觀察構造方法:
設置數據擴充閾值;
public HashMap() {
// all other fields defaulted
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
然後跳轉DEFAULT_LOAD_FACTOR
查看:
作用:容量的擴充閾值
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
通過源碼可以發現,每個Hashmap在對象實例化的時候都已經考慮到了數據存儲的擴充問題;
2) 觀察HashMap中的put方法
public V put(K key,V value){
return putVal(hash(key),key,value,false,true);
}
在使用put方法進行數據保存的時候會調用putVal方法,同時會將key進行哈希處理(生成hash碼)
而putVal()方法為了方便數據保存會將數據封裝為一個Node節點類對象,而在使用putVal()方法的操作過程會調用reasize()方法進行擴容;
3)容量擴充
當保存的數據超過既定的存儲容量則會進行擴容,原則如下:
常量地址:DEFAULT_INITIAL_CAPACITY
;作為初始化的容量配置,而後1向左移4為-》16;
常量的默認大小為16個元素,也就是說默認可以保存的最大的內容是16;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
當保存的數據內容超過了設置的閾值DEFAULT_LOAD_FACTOR = 0.75f
相當於容量x閾值 = 16*0.75 = 12;即保存到12個元素的時候就會進行容量擴充;
擴充的模式是2倍擴充:即每一次擴充2倍的容量。
4)大數據下的數據存儲方式:
在JDK1.8之後來到大數據時代,這就觸發了HashMap在大數據量中的訪問效率問題;
其中提供了一個重要的常量:TREEIFY_THRESHOLD
static final int TREEIFY_THRESHOLD = 8;
在使用HashMap進行保存的時候,如果保存的數據個數沒有超過閾值8,那麼會按照鏈表的形式進行數據的存儲;而超過了這個閾值,則會將鏈錶轉為紅黑樹以實現樹的平衡;並且利用左旋與右旋保證數據的查詢性能。
LinkedHashMap子類:
特點:基於鏈表形式實現偶對的存儲,可以保證存儲順序與數據增加的順序相同;
繼承結構:
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
使用LinkedHashMao子類存儲數據:
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子類存儲數據 {
public static void main(String[] args) {
Map<String,Integer> map = new LinkedHashMap<String,Integer>();
map.put("張三",1);
map.put("李四",2);
map.put("王五",3);
map.put(null,3);
map.put("趙六",null);
System.out.println(map);
}
}
運行可以發現集合的保存的順序與數據增加順序相同;同時LinkedHashMap子類允許保存的Key或value內容為null;
Hashtable子類:
其繼承結構如下:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable
使用Hashtable子類保存數據:
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子類存儲數據 {
public static void main(String[] args) {
Map<String,Integer> map = new Hashtable<String,Integer>();
map.put("張三",1);
map.put("李四",2);
System.out.println(map);
}
}
HashMap與Hashtable的區別:
HashMap中的 方法都是異步操作(非線程安全),HashMap中允許保存null數據
Hashtable中的方法都是同步操作(線程安全),但是效率慢,Hashtable不允許保存Null數據;否則會出現NUllpointException;
TreeMap子類:
特點:TreeMap屬於有序的Map集合類型;它可以按照key進行排序;所以需要Comaprable接口配合;
繼承結構如下:
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
TreeMap子類進行數據Key的排序:
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.Map;
import java.util.TreeMap;
public class TreeMap子類進行數據Key的排序 {
public static void main(String[] args) {
// 因為該程序保存的Key屬於String類型,由於String中實現了Comparable接口,所以
// 可以根據保存的字符的編碼進行由低到高的進行排序
/*
* public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
*
*
* */
Map<String,Integer> map = new TreeMap<String,Integer>();
map.put("C",3);
map.put("B",2);
map.put("A",1);
System.out.println(map);
}
}
Map.Entry內部接口:
在JDK1.9開始可以利用Map接口中創建Map.entry內部接口實例;
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.Map;
public class Map_Entry內部接口 {
public static void main(String[] args) {
Map.Entry<String,Integer> entry = Map.entry("One",1);
System.out.println(entry.getKey());
System.out.println(entry.getValue());
//觀察使用的子類
System.out.println(entry.getClass().getName());
}
}
在程序繼續寧Map.Entry對象構建的時,只傳入Key和Value就會自動利用KeyValueHolder子類實例化Map.Entry接口對象。
Iterator輸出Map集合:
集合數據輸出的標準形式是基於Iterator接口完成的;Collection接口直接提供iterator方法可以獲得iterator接口實例;但由於Map接口中保存的數據是多個Map.Entry接口封裝的二元偶對象,所以就必須採用Map集合的迭代輸出;
- 使得Map接口中的entrySet(),將Map集合變為Set集合;
- 取得Set接口實例後就可以利用iterator方法取得iterator的實例化對象;
- 使用iterator迭代找到每一個Map.Entry對象,並進行Key與Value的分。
Iterator和foreach輸出Map集合:
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Map集合的輸出問題 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("One",1);
map.put("two",2);
// 輸出的map中的Key與Value的值
Set<Map.Entry<String, Integer>> set = map.entrySet();//將Map集合轉變為Set集合
Iterator<Map.Entry<String,Integer>> iter = set.iterator(); //獲取Iterator接口
while(iter.hasNext()){
// Set中存的都是Map.Entry()對象
Map.Entry<String, Integer> me = iter.next();
System.out.print(me.getKey()+" "+me.getValue());
}
// System.out.println("\n");
//foreach循環輸出Map集合:
for(Map.Entry<String,Integer> entry:set){
System.out.print(entry.getKey()+" "+entry.getValue());
}
}
}
自定義Key類型:
採用自定義類的形式實現,但是作為Key類型的類由於存在數據查找的需求,所以必須在類中覆寫hashcode()與equals()方法。
package Java從入門到項目實戰.Java類集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
class Member{
private String name;
private int age;
public Member(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 boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Member)) return false;
Member member = (Member) o;
if (age != member.age) return false;
if (name != null ? !name.equals(member.name) : member.name != null) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
/*
* Key進行自定義;作為Key類型的類由於存在數據查找的需求,所以應該在類中覆寫HashCode() 和equals();
* */
public class 自定義Key的值 {
public static void main(String[] args) {
Map<Member,String> map = new HashMap<Member,String>();
map.put(new Member("張三",22),"xbhog");
map.put(new Member("李四",23),"博客");
map.put(new Member("王五",26),"welcome");
System.out.println(map.get(new Member("張三",22)));
}
}
在存儲大量的數據中,key還是可能出現重複的問題,這個問題叫Hash衝突;
解決的方式:
鏈地址法(拉鏈法)、開放尋址、再哈希、建立公共溢出區;
完結:
該博客字數6651,是迄今為止個人整理博客篇幅最長,字數最多的;希望讀者能有所收穫,也希望審核能過,選擇博客園第一感覺,園子里很舒服,學術氛圍很好,博文質量很高;不敢說自己的每篇文章寫的都可以,但是也注入了自己的心血,希望能上首頁!