import java.util.HashMap;import java.util.Map;import java.util.Set;
/*
Map接口实现类特点
1.Map与Collection并列存在,用于保存具有映射关系的数据 Key-Value
2.Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
3.Map中的key不允许重复,可为null,只能有一个
4.Map中的value可以重复,可为null,可以多个
5.常用String类作为Map的key
6.key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
7.一对k-v是放在一个HashMap$Node中的,因为Node实现了Entry接口,也说一堆k-v就是一个Entry
*/
@SuppressWarnings({"all"})
public class Map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1", "jack");
map.put("no2", "tom");
map.put("no1", "mary");//当有相同的key, 就等价于替换.
map.put("no3", "tom");//value可以重复
map.put(null, null);
map.put(null, "abc");//等价替换
map.put("no4", null);//value的null可以多个
map.put("no5", null);
map.put(new Object(), "smith");
// 通过 get 方法,传入 key ,会返回对应的 value
System.out.println(map.get("no2"));//tom
System.out.println("map=" + map);
//1.k-v最后是 HashMap$Node node = new Node(hash,key,value,null)
//2.为方便遍历,还会创建EntrySet集合,该集合存放的元素类型Entry
// 一个Entry对象就有k-v即: transient Set<Map.Entry<K,V>> entrySet;
//3.在entrySet中,定义类型是Map.Entry,但实际上还是HashMap$Node
// 因为 static class Node<K,V> implements Map.Entry<K,V>
//4.当把HashMap$Node对象存放到entrySet就方便了我们遍历
// 因为Map.Entry提供了方法 K getKey();V getValue()
Set set = map.entrySet();
System.out.println(set.getClass());//class java.util.HashMap$EntrySet
for (Object obj : set) {
// System.out.println(entry.getClass());//class java.util.HashMap$Node
//从HashMap$Node取出k-v
Map.Entry entry = (Map.Entry) obj;//向下转型
System.out.println(entry.getKey() + "-" + entry.getValue());
}
}
}
import java.util.HashMap;import java.util.Map;/*
Map接口常用方法
1.put //添加
2.remove //根据键删除
3.get //获取值
4.size //获取元素个数
5.isEmpty //判断是否为空
6.clear //清空
7.containsKey //查找键是否存在
*/
@SuppressWarnings({"all"})
public class Map02 {
public static void main(String[] args) {
Map map = new HashMap();
//put
map.put("001","jack");
map.put("002","tom");
map.put("003","smith");
System.out.println(map);//{001=jack, 002=tom, 003=smith}
//remove
map.remove("001");
System.out.println(map);//{002=tom, 003=smith}
//get
System.out.println(map.get("002"));//tom
//size
System.out.println(map.size());//2
//isEmpty
System.out.println(map.isEmpty());//false
//clear
map.clear();
System.out.println(map);//{}
//containsKey
System.out.println(map.containsKey("003"));//false
}
}
import java.util.*;/* containsKey:查找键是否存在
keySet:获取所有键
entrySet:获取所有关系
values:获取所有值
遍历方式
1.取出所有的key,通过key取出对应的value
增强for
迭代器
2.把所有values取出
增强for
迭代器
3.通过EntrySet获取
增强for
迭代器
HashMap小结
1.Map接口常用实现类:HashMap,Hashtable,Properties
2.HashMap是Map接口使用频率最高的实现类
3.HashMap是以key-value对的方式来存储数据(HashMap$Node)
4.与HashSet一样,不保证映射的顺序,因为底层以hash表的形式存储(数组+链表+红黑树)
5.HashMap没有实现同步,因此线程不安全
*/
@SuppressWarnings({"all"})
public class Map03 {
public static void main(String[] args) {
Map map = new HashMap();
map.put("001","jack");
map.put("002","tom");
map.put("003","smith");
//1.取出所有的key,通过key取出对应的value
Set set = map.keySet();
System.out.println("==========增强for=============");
for (Object key :set) {
System.out.println(key + "-" + map.get(key));
}
System.out.println("==========迭代器=============");
Iterator iterator1 = set.iterator();
while (iterator1.hasNext()) {
Object key = iterator1.next();
System.out.println(key + "-" + map.get(key));
}
//2.把所有values取出
Collection values = map.values();
System.out.println("==========增强for=============");
for (Object value :values) {
System.out.println(value);
}
System.out.println("==========迭代器=============");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
// 3.通过EntrySet获取
Set entrySet = map.entrySet();
System.out.println("==========增强for=============");
for (Object entry :entrySet) {
Map.Entry m = (Map.Entry) entry;//将entry转成Map.Entry,使用方法get()
System.out.println(m.getKey() + "-" + m.getValue());
}
System.out.println("==========迭代器=============");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
}
}
import java.util.HashMap;import java.util.Iterator;import java.util.Map;
import java.util.Set;
/*
练习
使用HashMap添加3个员工对象,要求键:员工id,值:员工对象
遍历显示工资>18000的员工
员工类:id、姓名、工资
*/
@SuppressWarnings({"all"})
public class Map04 {
public static void main(String[] args) {
Map map = new HashMap();
map.put(1,new Employee("tom",001,10000));
map.put(2,new Employee("jack",002,19999));
map.put(3,new Employee("jhon",003,20000));
Set set = map.keySet();
System.out.println("使用增强for");
for (Object key : set) {
//获取value
Employee employee = (Employee) map.get(key);
if (employee.getSalary() > 18000){
System.out.println(employee);
}
}
System.out.println("使用EntrySet");
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
//通过entry取得k-v
Employee employee = (Employee)entry.getValue();
if (employee.getSalary() > 18000){
System.out.println(employee);
}
}
}
}
class Employee{
private String name;
private int id;
private double salary;
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", id=" + id +
", salary=" + salary +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
import java.util.HashMap;/* HashMap底层
扩容机制和HashSet一致
1.HashMap底层维护了Node类型的数组table,默认为null
2.当创建对象时,将加载因子初始化为0.75
3.当添加key-value时,通过key的hash值得到在table的索引,判断该索引是否有元素
如果没有直接添加,如果有,则判断该元素的key和准备加入的key是否相等
如果相等,直接替换value,如果不相等,判断是树结构还是链表结构,做相应处理
如果容量不够,则扩容
4.第一次添加,扩容table容量为16,临界值为12(16*0.75)
5.以后再扩容,则扩容table容量为原来的两倍(16-32-64..),临界值为原来的两倍(12-24-48..)
6.在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认为8),且table大小>=MIN_TREEIFY_CAPACITY(默认64)
就会进行树化(红黑树),否则仍然采用数组扩容机制
*/
@SuppressWarnings({"all"})
public class Map05 {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("01","java");
map.put("02","c++");
map.put("01","python");//替换value
System.out.println(map);
}
}
/*
1.执行构造器 new HashMap()
初始化加载因子 loadFactor = 0.75
HashMap$Node[] table = null
2.执行put 调用hash方法,计算key的hash值
public V put(K key, V value) {//K="01",value="java"
return putVal(hash(key), key, value, false, true);
}
3.执行putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;;//辅助变量
//如果底层的 table 数组为 null, 或者 length =0 , 就扩容到 16
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//取出hash值对应的table的索引位置的Node,如果为null,就直接把加入的k-v创建成Node加入到该位置
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;//辅助变量
// 如果 table 的索引位置的 key 的 hash 值和新的 key 的 hash 值相同,
// 并 满足(table 现有的结点的 key 和准备添加的 key 是同一个对象 || equals 返回真)
// 就认为不能加入新的 k-v
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode))
//如果当前的 table 的已有的 Node 是红黑树,就按照红黑树的方式处理
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//如果找到的结点,后面是链表,就循环比较
for (int binCount = 0; ; ++binCount) {//死循环
if ((e = p.next) == null) {{//如果整个链表,没有和他相同,就加到该链表的最后
p.next = newNode(hash, key, value, null);
//加入后,判断当前链表的个数,是否已经到8,到8个就调用treeifyBin方法进行红黑树的转换
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//如果在循环比较过程中,发现有相同,就 break,就只是替换 value
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;//替换,key 对应 value
afterNodeAccess(e);
return oldValue;
}
}
++modCount;//每增加一个 Node ,就 size++
if (++size > threshold)//如 size > 临界值,就扩容
resize();
afterNodeInsertion(evict);
return null;
}
//如果 table 为 null ,或者大小还没有到 64,暂时不树化,而是进行扩容
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
*/
import java.util.Hashtable;/* HashTable
1.存放的元素是键值对,即K-V
2.HashTable的键和值都不能为null
3.HashTable使用方法基本和HashMap一致
4.HashTable是线程安全的,HashMap是线程不安全的
底层
1.有数组Hashtable$Entry[] 初始化为11
2.临界值为11*0.75=8
扩容
int newCapacity = (oldCapacity << 1) + 1; 即两倍+1
即 第二次扩容 11*2+1=23 第三次23*2+1=47 ...
临界值 23*0.75=17 47*0.75=35 ....
*/
@SuppressWarnings({"all"})
public class Map06 {
public static void main(String[] args) {
Hashtable table = new Hashtable();
table.put("john",100);
//table.put(null,100);//空指针异常NullPointerException
//table.put("smith",null);//空指针异常NullPointerException
table.put("jack",200);
table.put("john",300);//替换value
System.out.println(table);
}
}
import java.util.Properties;/* Properties
1.Properties类继承了Hashtable类且实现了Map接口,也是使用键值对的形式保存数据
2.使用特点和Hashtable类似
3.Properties还可用于从xxx.Properties文件中,加载数据到Properties类对象,并进行读取和修改
xxx.Properties文件通常为配置文件
*/
public class Map07 {
public static void main(String[] args) {
// Properties 继承 Hashtable,可以通过 k-v存放数据,key 和 value 不能为 null
// 增
Properties properties = new Properties();
//properties.put(null, "abc");//抛出 空指针异常
//properties.put("abc", null); //抛出 空指针异常
properties.put("john", 100);//k-v
properties.put("lucy", 100);
properties.put("jack", 100);
properties.put("john", 999);//相同key,value被替换
System.out.println(properties);
//删
properties.remove("lucy");
System.out.println(properties);
//改
properties.put("john", "smith");
System.out.println(properties);
//查
System.out.println(properties.get("jack"));
}
}
import java.util.Comparator;import java.util.TreeSet;/*
TreeSet
1.使用无参构造器,创建TreeSet时,仍然时无序的
2.使用有参构造器,传入一个比较器(匿名内部类),并指定排序规则
*/
@SuppressWarnings({"all"})
public class Map08 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
//使用无参构造器,创建TreeSet时,仍然时无序的
treeSet.add("tom");
treeSet.add("jack");
treeSet.add("smith");
treeSet.add("mary");
treeSet.add("jhon");
System.out.println(treeSet);
// 希望添加的元素按照字符串大小排序
TreeSet treeSet2 = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return (((String) o1).length() - ((String) o2).length());//按照长度大小排序,相同长度就加入不了
//return ((String) o1).compareTo((String) o2);//调用String的compareTo方法进行字符串大小比较
}
});
treeSet2.add("tom");
treeSet2.add("jack");
treeSet2.add("smith");
treeSet2.add("mary");//长度相同,加入不了
treeSet2.add("jhon");//长度相同,加入不了
System.out.println(treeSet2);
}
}
/*
1. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 在 调用 treeSet.add("tom"), 在底层会执行到if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果相等,即返回 0,这个 Key 就没有加入
return t.setValue(value);
} while (t != null)
*/
import java.util.Comparator;import java.util.TreeMap;/*
TreeMap
1.使用无参构造器,创建TreeSet时,仍然时无序的
2.使用有参构造器,传入一个比较器(匿名内部类),并指定排序规则
*/
@SuppressWarnings({"all"})
public class Map09 {
public static void main(String[] args) {
TreeMap treeMap = new TreeMap();
treeMap.put("jack","杰克");
treeMap.put("mary","玛丽");
treeMap.put("tom","汤姆");
treeMap.put("smith","史密斯");
System.out.println(treeMap);
TreeMap treeMap1 = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// return ((String) o1).compareTo((String) o2);//按照Key(String)的大小比较
return (((String) o1).length() - ((String) o2).length());//按照长度比较,相同长度就替换
}
});
treeMap1.put("jack","杰克");
treeMap1.put("mary","玛丽");//替换杰克
treeMap1.put("tom","汤姆");
treeMap1.put("smith","史密斯");
System.out.println(treeMap1);
}
}
/*
1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
2. 调用 put 方法
2.1 第一次添加, 把 k-v 封装到 Entry 对象,放入 root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
2.2 以后添加
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do { //遍历所有的 key , 给当前 key 找到适当位置
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的 compare
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不添加
return t.setValue(value);
} while (t != null);
*/