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); */