Map(映射,存儲(chǔ)的是“鍵-值”映射表,“鍵”是不能重復(fù)的)
如果鍵重復(fù),則相當(dāng)于修改對(duì)應(yīng)鍵的值。
Map接口定義方法
int size();//獲取map大小
boolean isEmpty();//size==0?
boolean containsKey(Object var1);//查看是否包含某個(gè)鍵
boolean containsValue(Object var1);//查看是否包含某個(gè)值
V get(Object var1);//跟據(jù)key獲取值
V put(K var1, V var2);//Map接口有兩個(gè)類型參數(shù),K和V,分別表示鍵(Key)和值(Value)的類型,按Key值var1保存值var2,如果鍵重復(fù),則相當(dāng)于修改對(duì)應(yīng)鍵的值。
V remove(Object var1);//移除鍵對(duì)應(yīng)的值
void putAll(Map<? extends K, ? extends V> var1);//批量保存
void clear();//清空map
Set<K> keySet();//獲取Map中鍵的集合
Collection<V> values();//獲取Map中值的集合
Set<Map.Entry<K, V>> entrySet();//獲取Map中值的鍵值對(duì)(遍歷)
public interface Entry<K, V> {
K getKey();//鍵
V getValue();//值
V setValue(V var1);//修改鍵
boolean equals(Object var1);
int hashCode();
}
方法使用簡(jiǎn)介(以HashMap實(shí)現(xiàn)類為例)
HashMap 存儲(chǔ)的數(shù)據(jù)是沒有順序的,鍵或值可以為null
/**
* Map
*/
public class Demo {
public static void main(String[] args) {
//key //value//Map<K,V>
Map<Integer, String> map = new HashMap<>();
map.put(1, "aaa");//添加元素
map.put(2, "www");
map.put(3, "assaa");
map.put(4, "qqq");
map.put(5, "ggg");
map.put(1, "nnn");//修改元素
map.put(6, "ooo");
map.put(7, "232");
map.put(12, "ffds");
System.out.println(map.size() + "");//獲取大小//8
//////////////////////
System.out.println(map.isEmpty());//size==0?//false
//////////////////////
System.out.println(map.containsKey(10));//查看是否包含某個(gè)鍵//false
/////////////////////
System.out.println(map.containsValue("ooo"));//查看是否包含某個(gè)值//true
/////////////////////
System.out.println(map.get(3));//跟據(jù)key獲取值//assaa
/////////////////////
map.remove(12);//移除鍵對(duì)應(yīng)的值
System.out.println(map.containsKey(12));//false
/////////////////////
Map<Integer, String> map1 = new HashMap<>();
map.put(100, "999");//添加元素
map.put(110, "222");//添加元素
map.put(120, "333");//添加元素
map.putAll(map1);//批量保存
Set<Integer> integers = map.keySet();//獲取Map中鍵的集合
System.out.println(integers);//[1, 2, 3, 4, 100, 5, 6, 7, 120, 110]
////////////////////////
Collection<String> values = map.values();//獲取Map中值的集合
System.out.println(values);//[nnn, www, assaa, qqq, 999, ggg, ooo, 232, 333, 222]
////////////////////////
Set<Map.Entry<Integer, String>> entries = map.entrySet();//迭代
Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> mapNext = iterator.next();
System.out.print(mapNext.getKey() + "-->" + mapNext.getValue() + "\t");
//1-->nnn 2-->www 3-->assaa 4-->qqq 100-->999 5-->ggg 6-->ooo 7-->232 120-->333 110-->222
}
}
}
額外延伸SparseArray
Hashtable
Hashtable和HashMap的區(qū)別
- Hashtable里面的方法幾乎都是同步的,線程安全,HashMap則沒有,但效率高。(同ArrAyList和Vector)
- Hashtable不允許存放null值(鍵和值都不可以),而HashMap可以
相同點(diǎn)
- 存放元素?zé)o序
LinkedHashMap繼承自HashMap
- LinkedHashMap 實(shí)現(xiàn)與 HashMap 的不同之處在于,LinkedHashMap 維護(hù)著一個(gè)運(yùn)行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,
該迭代順序可以是插入順序或者是訪問(wèn)順序(參考以下代碼理解) - 不是線程安全
案例
/**
* LinkedHashMap
* LinkedHashMap和HashMap區(qū)別
* LinkedHashMap 保存了記錄的插入順序
* HashMap 則無(wú)序存放
*/
public class Demo {
public static void main(String[] args) {
//關(guān)注點(diǎn)一:看添加之后的輸出結(jié)果對(duì)比
System.out.println("關(guān)注點(diǎn)一:LinkedHashMap輸出:");
LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap();
linkedHashMap.put(1, "aaa");//添加元素
linkedHashMap.put(7, "www");
linkedHashMap.put(5, "ggg");
linkedHashMap.put(1, "nnn");//修改元素
linkedHashMap.put(6, "ooo");
linkedHashMap.put(7, "232");
linkedHashMap.put(12, "ffds");
Set<Map.Entry<Integer, String>> entries = linkedHashMap.entrySet();
Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> next = iterator.next();
System.out.print(next.getKey() + "-->" + next.getValue() + "\t");
//1-->nnn 7-->232 5-->ggg 6-->ooo 12-->ffds
}
System.out.println();
System.out.println("關(guān)注點(diǎn)一:HashMap輸出:");
Map<Integer, String> map = new HashMap<>();
map.put(1, "aaa");//添加元素
map.put(7, "www");
map.put(5, "ggg");
map.put(1, "nnn");//修改元素
map.put(6, "ooo");
map.put(7, "232");
map.put(12, "ffds");
Set<Map.Entry<Integer, String>> entries1 = map.entrySet();//迭代
Iterator<Map.Entry<Integer, String>> iterator1 = entries1.iterator();
while (iterator1.hasNext()) {
Map.Entry<Integer, String> mapNext = iterator1.next();
System.out.print(mapNext.getKey() + "-->" + mapNext.getValue() + "\t");
//1-->nnn 5-->ggg 6-->ooo 7-->232 12-->ffds
}
/////////////////////////////////////////////////////////////////
//關(guān)注點(diǎn)二:換一個(gè)構(gòu)造方法
System.out.println(); //10 大小//0.75還不清楚//true代表使用訪問(wèn)順序
LinkedHashMap<Integer, String> linkedHashMap1 = new LinkedHashMap<>(10, 0.75f, true);
linkedHashMap1.put(1, "aaa");//添加元素
linkedHashMap1.put(7, "www");
linkedHashMap1.put(5, "ggg");
linkedHashMap1.put(1, "nnn");//修改元素
linkedHashMap1.put(6, "ooo");
linkedHashMap1.put(7, "232");
linkedHashMap1.put(12, "ffds");
System.out.println("=======================================================");
System.out.println("更換構(gòu)造方法后未使用元素之前輸出");
Set<Map.Entry<Integer, String>> entries2 = linkedHashMap1.entrySet();
Iterator<Map.Entry<Integer, String>> iterator2 = entries2.iterator();
while (iterator2.hasNext()) {
Map.Entry<Integer, String> next = iterator2.next();
System.out.print(next.getKey() + "-->" + next.getValue() + "\t");
//5-->ggg 1-->nnn 6-->ooo 7-->232 12-->ffds
}
System.out.println();
//關(guān)鍵點(diǎn)來(lái)了
System.out.println(linkedHashMap1.get(6));//ooo
System.out.println(linkedHashMap1.get(12));//ffds
System.out.println("更換構(gòu)造方法后未使用元素之后輸出");
Set<Map.Entry<Integer, String>> entries3 = linkedHashMap1.entrySet();
Iterator<Map.Entry<Integer, String>> iterator3 = entries3.iterator();
while (iterator3.hasNext()) {
Map.Entry<Integer, String> next = iterator3.next();
System.out.print(next.getKey() + "-->" + next.getValue() + "\t");
//5-->ggg 1-->nnn 7-->232 6-->ooo 12-->ffds
}
}
}
運(yùn)行結(jié)果:
LinkedHashMap延伸(用途)
最近最少使用LRUcache
最近最少使用LRUcache
最近最少使用LRUcache
最近最少使用LRUcache
TreeMap
使用了二叉權(quán)的數(shù)據(jù)結(jié)構(gòu),key是有序,保存其唯一性用到了hashCode()、equals()以及比較器(唯一性判斷,鍵排序同TreeSet)
案例
/**
* TreeMap:
* 使用了二叉權(quán)的數(shù)據(jù)結(jié)構(gòu),key是有序,保存其唯一性用到了hashCode()、equals()以及比較器(唯一性判斷同HashSet)
*/
public class Demo {
public static void main(String[] args) {
//來(lái)個(gè)稍微復(fù)雜點(diǎn)的:存放一個(gè)Student鏈表
//TreeMap<K,V>K類必須實(shí)現(xiàn)Comparable<T>接口,用于比較排序
TreeMap<String, List<Student>> map = new TreeMap<>();
List<Student> students1 = new ArrayList<>();
students1.add(new Student("小花", 23));
students1.add(new Student("小黑", 20));
students1.add(new Student("小魚", 29));
students1.add(new Student("小小", 23));
map.put("小班", students1);
List<Student> students2 = new ArrayList<>();
students2.add(new Student("大花", 230));
students2.add(new Student("大黑", 200));
students2.add(new Student("大魚", 290));
students2.add(new Student("大大", 230));
map.put("大班", students2);
Set<Map.Entry<String, List<Student>>> entries = map.entrySet();
for (Map.Entry<String, List<Student>> entry : entries) {
List<Student> s = entry.getValue();
System.out.println(entry.getKey() + ":" + s);
}
}
}
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "[" + this.name + ":\t" + this.age + "]";
}
}
運(yùn)行結(jié)果
案例二
/**
* TreeMap自定義比較器
* <p>
* 案例:按地區(qū)存放學(xué)校
* 建模
* //School
* //Area
*/
public class Demo {
public static void main(String[] args) {
List<School> schools1 = new ArrayList<School>();
schools1.add(new School("10", "火星1"));
schools1.add(new School("11", "火星2"));
schools1.add(new School("12", "火星3"));
List<School> schools2 = new ArrayList<School>();
schools2.add(new School("20", "北京1"));
schools2.add(new School("21", "北京2"));
schools2.add(new School("22", "北京3"));
//如果TreeMap<K,V>的K是自定義類型 ,則此類必須實(shí)現(xiàn)Comparable<T>接口,用于比較排序
Map<Area, List<School>> clsMap = new TreeMap<Area, List<School>>();
clsMap.put(new Area("1004", "火星"), schools1);
clsMap.put(new Area("1002", "北京"), schools2);
Set<Map.Entry<Area, List<School>>> entrySet = clsMap.entrySet();
for (Map.Entry<Area, List<School>> cls : entrySet) {
List<School> s = cls.getValue();
System.out.println(cls.getKey().id+"\t" + cls.getKey().name + ":" + s);
}
}
}
//
class Area implements Comparable<Area> {
//名字
String name;
//編號(hào)
String id;
public Area(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public int compareTo(Area o) {
//先比較班級(jí)的名稱,如果名稱相同,再比較id(也可以先比較id再比較name)
int r = this.name.compareTo(o.name);
return r == 0 ? this.id.compareTo(o.id) : r;
}
@Override
public String toString() {
return this.name + "\t";
}
}
class School {
private String id;
private String name;
public School(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "School[id=" + id + ", name=" + name + "]";
}
}
運(yùn)行結(jié)果
補(bǔ)充
List、Set、Map是否繼承自Collection接口?
List、Set 是,Map 不是。Map是鍵值對(duì)映射容器,與List和Set有明顯的區(qū)別,而Set存儲(chǔ)的零散的元素且不允許有重復(fù)元素(數(shù)學(xué)中的集合也是如此),List是線性結(jié)構(gòu)的容器,適用于按數(shù)值索引訪問(wèn)元素的情形。
闡述ArrayList、Vector、LinkedList的存儲(chǔ)性能和特性。
ArrayList 和Vector都是使用數(shù)組方式存儲(chǔ)數(shù)據(jù),此數(shù)組元素?cái)?shù)大于實(shí)際存儲(chǔ)的數(shù)據(jù)以便增加和插入元素,它們都允許直接按序號(hào)索引元素,但是插入元素要涉及數(shù)組元素移動(dòng)等內(nèi)存操作,所以索引數(shù)據(jù)快而插入數(shù)據(jù)慢,Vector中的方法由于添加了synchronized修飾,因此Vector是線程安全的容器,但性能上較ArrayList差,因此已經(jīng)是Java中的遺留容器。LinkedList使用雙向鏈表實(shí)現(xiàn)存儲(chǔ)(將內(nèi)存中零散的內(nèi)存單元通過(guò)附加的引用關(guān)聯(lián)起來(lái),形成一個(gè)可以按序號(hào)索引的線性結(jié)構(gòu),這種鏈?zhǔn)酱鎯?chǔ)方式與數(shù)組的連續(xù)存儲(chǔ)方式相比,內(nèi)存的利用率更高),按序號(hào)索引數(shù)據(jù)需要進(jìn)行前向或后向遍歷,但是插入數(shù)據(jù)時(shí)只需要記錄本項(xiàng)的前后項(xiàng)即可,所以插入速度較快。Vector屬于遺留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遺留容器),已經(jīng)不推薦使用,但是由于ArrayList和LinkedListed都是非線程安全的,如果遇到多個(gè)線程操作同一個(gè)容器的場(chǎng)景,則可以通過(guò)工具類Collections中的synchronizedList方法將其轉(zhuǎn)換成線程安全的容器后再使用
Collection和Collections的區(qū)別?
Collection是一個(gè)接口,它是Set、List等容器的父接口;Collections是個(gè)一個(gè)工具類,提供了一系列的靜態(tài)方法來(lái)輔助容器操作,這些方法包括對(duì)容器的搜索、排序、線程安全化等等
List、Map、Set三個(gè)接口存取元素時(shí),各有什么特點(diǎn)?
List以特定索引來(lái)存取元素,可以有重復(fù)元素。Set不能存放重復(fù)元素(用對(duì)象的equals()方法來(lái)區(qū)分元素是否重復(fù))。Map保存鍵值對(duì)(key-value pair)映射,映射關(guān)系可以是一對(duì)一或多對(duì)一。