Map<K,V>(I)
1.Map是最大的接口,也就是说往上没有父接口了,也就意味着没有继承Iterable接口,所以不能使用迭代器进行遍历.
2.特点:
1).Map是一个映射表,一个双列集合,一个元素包含两个值(key - value)
2).key(键)是唯一的,value(值)是允许重复的
3).因为key是唯一的,所以可以根据key获得value
3.Map集合的分支
| - HashMap©
|- LinkedHashMap©
| - HashTable©
| SortedMap(I)
| - TreeMap©
| - ConcurrentHashMap©
4.HashMap©
1).底层实现: 哈希表(散列表)
底层数据结构:
JDK1.8之前: 数组 + 单向链表
JDK1.8之后: 数组 + 单向链表|红黑树
红黑树图解
2).特点:
a.是一个无序的集合,存储元素和取出元素的顺序有可能是不一致
b.如果key完全一样,则value会被覆盖,等同于修改元素(value)
c.HashMap的key允许只有一个null值,但是value可以允许有多个null值
3)存储过程:
a.计算key的hashCode值,确定位置
b.判断位置上是有存在元素
c.如果没有元素,则直接存入
d.如果有元素,则使用equals进行判断是否一致
需要跟该位置上的链表中所有元素进行对比
e.如果一致,则进行覆盖
f.如果不一致,存入到链表尾部(头部)
注意:在JDK1.8以后,如果链表长度超过8,则变成红黑树进行存储.
4)初始容量大小
a.默认初始容量: 16
b.要进行扩容时:
* 如果存满再扩容,会造成链表多,效率低下
* 如果是过半就扩容,会占用内存空间
* 加载因子: 扩容条件
默认0.75 -> 容量达到75%进行扩容
扩容为原容量的2倍
使用的方法resize()
5)常用API:
V put(K key, V value)
V get(Object key)
V remove(Object key)
containsKey(Object key)
containsValue(Object value)
putAll(Map<? extends K,? extends V> m)
package api06.map;
import java.util.HashMap;
/*
HashMap常用API
*/
public class MapDemo01 {
public static void main(String[] args) {
/*
V put(K key, V value)
在此映射中关联指定值与指定键。
*/
//创建HashMap的对象
HashMap<String, Integer> map = new HashMap<>();
//键和值 组成 一个元素
Integer v1 = map.put("小乔", 16);
System.out.println("v1:" + v1);//v1:null
Integer v2 = map.put("小乔", 17);
//如果是相同的key ,那么value会进行覆盖
System.out.println("v2:" + v2);//v2:16
System.out.println(map);//{小乔=17}
//{null=18, 貂蝉=18, 小乔=17}
map.put(null, null);
map.put("貂蝉",null);
//{貂蝉=18, 小乔=17, 周瑜=18}
System.out.println(map);
}
}
package api06.map;
import java.util.HashMap;
/*
HashMap常用API
*/
public class MapDemo02 {
public static void main(String[] args) {
/*
V remove(Object key)
从此映射中移除指定键的映射关系(如果存在)。
*/
HashMap<String, Integer> map = new HashMap<>();
map.put("小乔",17);
map.put("周瑜",18);
map.put("大乔",19);
//删除 key = 小乔
Integer v1 = map.remove("小乔");
System.out.println("v1:" + v1);//v1:17
//{大乔=19, 周瑜=18}
System.out.println(map);
//删除不存在的元素 - key
Integer v2 = map.remove("张飞");
//NullPointerException - 自动拆箱
System.out.println("v2:" + v2);//v2:null
System.out.println(map);//{大乔=19, 周瑜=18}
System.out.println("---------------------------");
/*
V get(Object key)
返回指定键所映射的值;如果对于该键来说,
此映射不包含任何映射关系,则返回 null。
*/
HashMap<String, Integer> map1 = new HashMap<>();
map1.put("小乔",17);
map1.put("周瑜",18);
map1.put("大乔",19);
Integer v3 = map1.get("小乔");
System.out.println("v3:" + v3);//v3:17
Integer v4 = map1.get("张飞");
System.out.println("v4:" + v4);//v4:null
}
}
5.Map集合的遍历
Set keySet()
返回此映射中包含的键的Set 集合
注意: 获得所有的key值,并且获得对应的Set类型集合
Collection values()
返回此映射中包含的值的 Collection集合。
注意: 获取所有的value值,并且获得对应的Collection
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set集合。
<Map.Entry<K,V>: 作为Map的内部接口存在
Entry<K,V>: 键值对
将entry作为一个元素,存入到Set集合中
getKey() -> 获取键
getValue() -> 获取值
package api06.map;
import java.util.*;
/*
Map的遍历/迭代
*/
public class MapDemo03 {
public static void main(String[] args) {
/*
Set<K> keySet()
返回此映射中包含的键的 Set集合。
*/
HashMap<String, Integer> map = new HashMap<>();
map.put("小乔",17);
map.put("周瑜",18);
map.put("大乔",19);
map.put("张飞",18);
map.put("吕布",19);
System.out.println(map);
//取出所有的元素
Set<String> set = map.keySet();
//对set集合进行遍历 - foreach / 迭代器遍历
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.print("," + s);//获取到的key值
//通过调用get方法 -> 可以得到value
Integer v1 = map.get(s);
System.out.println(v1);
}
//foreach - 简易版迭代器
for ( String s : set ){
System.out.println(s);
//通过调用get方法 -> 可以得到value
Integer v1 = map.get(s);
System.out.println(v1);
}
//foreach - 简易版迭代器 - 优化
for ( String s : map.keySet() ){
System.out.println(s);
//通过调用get方法 -> 可以得到value
Integer v1 = map.get(s);
System.out.println(v1);
}
System.out.println("--------------------");
/*
方式二: Collection<V> values()
返回此映射中包含的值的 Collection集合。
特点: 只能获取到value值,不能通过value获取key
*/
Collection<Integer> col = map.values();
for(Integer i : col){
System.out.print(i + ",");
System.out.println();
}
System.out.println("------------------");
/*
方式三: Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set集合。
Map.Entry<K,V> - 键值对
*/
Set<Map.Entry<String, Integer>> set1 = map.entrySet();
for (Map.Entry entry: set1){
//获取entry中key
Object s = entry.getKey();
//获取entry中value
Object v = entry.getValue();
System.out.println(s + "= " + v);
}
}
}
计算⼀个字符串中每个字符出现次数练习
package api06.map;
import java.util.HashMap;
/*
需求:
计算⼀个字符串中每个字符出现次数。
1.获得一个字符串(Scanner)
2.创建Map集合,Key值是字符,value值是字符个数
3.遍历字符串 -> 获得字符
4.对字符进行判断,判断该字符是否存在map集合中
如果Key存在:
value++
如果key不存在:
put(c,1)
判断key是否存在:
1.containKey()
2.get(key)
*/
public class MapWorkTest05 {
public static void main(String[] args) {
// 1.获得一个字符串(Scanner)
String str = "aaaabbbccd";
//2.创建Map集合,Key值是字符,value值是字符个数
HashMap<Character, Integer> map = new HashMap<>();
//3.遍历字符串 -> 获得字符
for (Character c : str.toCharArray()){
//4.对字符进行判断,判断该字符是否存在map集合中
if(map.containsKey(c)){
//c -> key
//想要获取的是value值
Integer value = map.get(c);
value++;//个数+1
map.put(c,value);
}else{
map.put(c,1);
}
}
//遍历集合获取值
for (Character c :map.keySet()) {
Integer v = map.get(c);
System.out.println(c + "," + v);
}
}
}
6.LinkedHashMap©
1)底层实现: 数组 + 双向链表
2)特点:
是一个有序的集合,存储元素和删除元素都是一致的
package api06.map;
import java.util.LinkedHashMap;
/*
LinkedHashMap的使用
插入有序
*/
public class LinkedHashMapDemo06 {
public static void main(String[] args) {
//{小乔=18, 大桥=12, 张飞=15, 周瑜=20}
LinkedHashMap<String,Integer> map = new LinkedHashMap();
map.put("小乔",18);
map.put("大桥",12);
map.put("张飞",15);
map.put("周瑜",20);
System.out.println(map);
}
}
7.HashTable©
1)特点:
a.继承Dictionary类
b.线程安全的,效率较低
c.键和值都不允许有null值
2)子类:
Properties - 集合中唯一和IO流有有关的集合
作为配置文件使用
package api06.map;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
/*
HashTable的使用
*/
public class HashTableDemo07 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap();
map.put(null,18);
map.put("大桥",12);
map.put("张飞",null);
map.put("周瑜",null);
System.out.println(map);
Hashtable<String, Integer> map1 = new Hashtable<>();
//key存在null的情况
//map1.put(null,18);//NullPointerException
map1.put("大桥",12);
//value存在null
map1.put("周瑜",null);//NullPointerException
System.out.println(map1);
}
}
8.TreeMap©
作用: 对Map进行排序
package api06.map;
import java.util.Comparator;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
/*
TreeMap的使用 - 排序
排序:
1.自然排序
实现接口: Comparable -> 重写方法: compareTo()
2.自定义排序(比较器排序)
实现接口: Comparator -> 重写方法: compare()
*/
public class TreeMapDemo08 {
public static void main(String[] args) {
TreeMap map = new TreeMap();
map.put("小乔",18);
map.put("大桥",12);
map.put("张飞",15);
map.put("周瑜",20);
System.out.println(map);
TreeMap<String,Integer> tree = new TreeMap(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
tree.put("小乔",18);
tree.put("大桥",12);
tree.put("张飞",15);
System.out.println(tree);
}
}
9.ConcurrentHashMap©
1)底层实现:
JDK1.7 数组(segment数组) + (Entry)链表
分段锁技术
底层数据结构:数组 + 链表
JDK1.8 CAS + Synchronized
->Compare And Swap
底层数据结构:数组 + 链表|红黑树
10.HashMap和hashTable和ConCurrentHashMap之间的区别
1)线程安全问题
2)父类/父接口
3)是否允许存在null
4)底层实现
11.JDK1.9的新特性 -> of
1)List接口,Set接口,Map接口里边新增的一个静态方法 - of,可以给集合一次性添加多个元素
2)注意:
a. of()只能适用于List接口,Set接口,Map接口,不适用接口的实现类
b. of()的返回值是一个不可改变的集合,否则会抛出异常:UnsupportedOperationException:不支持的操作异常
c. Set/Map集合在调用of()的时候,不能有重复值,否则会抛出异常: IllegalArgumentException: 非法的参数异常
package api06.map;
import java.lang.reflect.Array;
import java.util.*;
/*
JDK1.9新特性
*/
public class JDK9Demo09 {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(1);
list.add(1);
//of -> 使用静态方法调用
//List<Integer> list1 = List.of(1, 3, 4, 5, 6);
//UnsupportedOperationException - 不支持的操作异常
//list1.add(1);
//System.out.println(list1);
HashMap<String, Integer> map = new HashMap<>();
map.put("小乔",17);
map.put("小乔",17);
map.put("小乔",17);
map.put("小乔",17);
//使用Map的静态方法调用
/*给的是相同的key值
IllegalArgumentException: 非法的参数异常
*/
//Map.of("xiapqio",1,"xiapqio",2);
//Map.of("xiapqio",1,"张三",2);
System.out.println(map);
//不允许有重复值
//IllegalArgumentException - 非法的参数异常
//Set<Integer> set = Set.of(2, 2, 3);
//Set<Integer> set = Set.of(2,3);
//System.out.println(set);
}
}
测试方法:
System.out.println() --> 打桩测试
Debug --> 断点测试
JUnit --> 单元测试
JUnit --> 以方法为单位,可以直接取代主方法
导入第三方jar包的步骤:
1.在工程中创建一个文件夹lib
2.将要导入的jar包进行复制到lib文件夹中
3.右键选中jar包, Add as Library 添加依赖
自定义类
package api06.map;
import java.util.Objects;
/*
自定义类
*/
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
User user = (User) object;
return age == user.age &&
Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, 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;
}
}