
- V put (K key,V value) 向Map集合中添加键值对- V get (Object key) 通过key获取value- V remove(Object key) 通过key删除某键值对- void clear() 清空Map集合- boolean containsKey(Object key) 判断Map中是否包含某个key- boolean containsValue(Object value) 判断Map中是否包含某个value- boolean isEmpty() 判断Map集合中元素个数是否为0- Set keySet() 获取Map集合中所有的key,所有的键是一个Set集合- V remove (Object key) 通过key删除键值对- int size() 获取Map集合中键值对的个数- Collection values() 获取Map集合中所有的value,返回一个Collection- Set
举例:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class Map1 {public static void main(String[] args) {//即Key为Integer,Value为StringMap map = new HashMap<>();//这里的1自动装箱map.put(1,"A");map.put(2,"B");map.put(3,"C");//"A"String value = map.get(1);//3System.out.println(map.size());map.remove(2);//falseSystem.out.println(map.containsKey(2));//falseSystem.out.println(map.containsValue("B"));//trueSystem.out.println(map.containsValue(new String("A")));Collection values = map.values();//A、Cfor(String s:values){System.out.println(s);}Set key = map.keySet();//1、3for(Integer s:key){System.out.println(s);}map.clear();//trueboolean e = map.isEmpty();//Map转SetSet> set = map.entrySet();}
}
Set
//理解一下最后这个Map转Set的类型import java.util.HashSet;
import java.util.Set;public class MyClass {//静态内部类声明private static class InnerClass{public static void m1(){}public void m2(){}}public static void main(String[] args) {//调用静态内部类中的静态方法MyClass.InnerClass.m1();//创建静态内部类对象MyClass.InnerClass mi = new MyClass.InnerClass();mi.m2();//类比Set set =new HashSet<>();//简单的看就和Set set = new HashSet<>();一样// 就是个类型}
}
即,Map集合通过entrySet方法转换成Set集合后,Set集合中元素的类型是Map.Entry,再加个泛型
//抛开泛型,就明晰很多:
Set set = map.entrySet();
遍历方式一:获取所有的key,通过key来遍历value
Set mySet = map.keySet();
Iterator iterator = mySet.iterator();
//遍历获取Set集合的方式,也可下标和增强for
while(iterator.hasNext()){Integer myKey = iterator.next();String myValue = map.get(myKey);System.out.println("Key:" + myKey + ",value:" + myValue);
}//增强for
for(Integer myKey:mySet){System.out.println("Key:" + myKey + ",value:" + map.get(myKey));
}
遍历方式二:把Map集合转化为Set集合
n个Map中的键值对,对应Set中n个Map.Entry

Iterator> iterator = set.iterator();
//迭代器遍历
while(iterator.hasNext()){Map.Entry node = iterator.next();Integer myKey = node.getKey();String myValue = node.getValue();System.out.println(myKey + "=" + myValue);
}
//foreach
for(Map.Entry node:set){System.out.println(node.getKey() + "=" + node.getValue());
}
其中,foreach的遍历效率较高,因为获取key和value都是从node对象中获取属性值,适用与大量数据。
另外,直接打印Set集合也可以输出key=value的形式,应该是Map.Entry类重写了toString吧
Set> set = map.entrySet();
for(Map.Entry s:set){System.out.println(s);
}
三种方式的运行结果一样:

哈希表是一个数组和单向链表的结合体。数组在查询方面效率很高,而随机增删效率低。单向链表则是随机增删效率高而查询效率低,哈希表则是融合两种数据结构,充分发挥二者的优点。

//HashMap集合底层源码精简:
public class HashMap{//HashMap底层实际是一个一维数组Node[] table;//静态的内部类static class Node{//哈希值,是此处key的hashCode()方法的执行结果//hash值通过哈希函数可以转换为数组下标final int hash;//存到Map中的keyfinal K key;//存到Map集合中的valueV value;//下一个节点的内存地址Node next;}
}

v = map.get(k)方法的实现原理
基本思路:
类比字典查”中“字,由zhong先锁定范围,再去一页页的找,而不是从第一页开始一页页的找
实现步骤:
map.put(k,v)方法的实现原理
实现步骤:
从最后一步,也可以看出,HashMap集合的key部分是无序且不可重复。无序即写入时根据k,不一定会挂到哪个链表上(即不是齐刷刷挂过去的),这样写入和读取的时候就是两个顺序。不可重复即由equals和value的覆盖来保证。
复习Tip:- 放到HashSet集合当中的元素,实际是放到HashMap集合的key部分了。
根据HashMap的结构图,HashMap集合使用不当时,不能发挥其性能。
😉 不当情况1:
所有的hashCode方法返回值均为固定值A,则哈希表变成了纯单向链表,这种情况称散列分布不均匀。形象的说就是全挂一根线上了。

😉 不当情况2:
所有的hashCode方法返回值均不同,即数组下标都不同,则哈希表变成了一维数组,也是散列分布不均匀。

根据上面HashMap集合的存取,可知必须重写hashCode和equals方法。需要重写的是hashMap集合的key部分的元素,以及放在HashSet集合中的元素。

- HashMap集合的默认初始化容量是16,默认加载因子是0.75,即当HashMap集合底层数组的容量达到75%时,数组开始扩容
- HashMap集合的初始化容量必须是2的次幂,a power of 2,这样可以提高HashMap集合的存取效率,达到散列均匀
定义一个Student类,并重写equals:

由此:
一个类的equals方法重写了,则hashCode方法也要重写,且"equals方法返回值为true时,hashCode方法的返回值必须一样"。
🍁JDK8对HashMap集合的改进:
static final int TREEIFY_THRESHOLD = 8;
即JDK8之后,哈希表单向链表中的元素超过8个时,单向链表的数据结构变为红黑树
static final int UNTREEIFY_THRESHOLD = 6;
当红黑树上的节点数量小6时,红黑树重新变成单向链表,以上变化是为了提高检索效率
🍁哈希碰撞
🍁HashMap的扩容
🍁HashMap集合的key允许为null
Map map2 = new HashMap();
map2.put(null,null);
System.out.println(map2.size()); //1
map2.put(null,100); //key重复,value覆盖
System.out.println(map2.size()); //1
System.out.println(map2.get(null)); // 100
Map map3 = new Hashtable();
//java.lang.NullPointerException
map3.put(null,"123");
//java.lang.NullPointerException
map3.put("999",null);
Hashtable.class中的源码片段:
// Make sure the value is not nullif (value == null) {throw new NullPointerException();}
区别:
Properties类继承Hashtable,属于Map集合,Properties集合的key和value都是String类型。Properties被称为属性类对象,Properties是线程安全的。
Properties properties = new Properties();
//存:
//setProperty(String key,String value)方法
//取:
//getProperty(String key)方法Properties properties = new Properties();
properties.setProperty("username","root");
properties.setProperty("password","123456");
String username = properties.getProperty("username");
//setProperty方法调用Hashtable的put方法
public synchronized Object setProperty(String key, String value) {return put(key, value);
}

//应用场景:数据库中取数据,页面上需要升序或者降序,放进TreeSet再拿出来
TreeSet set2 = new TreeSet<>();
set2.add("zhangsan");
set2.add("lisi");
set2.add("wangwu");
for(String data:set2){System.out.println(data);
}
//按字母升序输出
//String类已实现了Comparable接口
TreeSet集合排序的前提是元素的类已经实现了Comparable接口 ,对自定义的类,要注意这一点,否则java.lang.ClassCastException
//未实现Comparable
class Person{int age;public Person(){}public Person(int age){this.age = age;}public String toString(){return "age=" + this.age;}
}
Person p1 = new Person(22);
Person p2 = new Person(30);
Person p3 = new Person(40);
TreeSet treeSet = new TreeSet<>();
treeSet.add(p1);
treeSet.add(p2);
treeSet.add(p3);
for(Person p :treeSet){System.out.println(p);
}//运行报错:
java.lang.ClassCastException: class Person cannot be cast to class java.lang.Comparable
以上是因为:往TreeSet中存时,要排序,而Person这个自定义的类无法排序,因为没有指定Person对象之间谁大谁小的规则,即没有实现java.lang.Comparable接口
实现Comparable接口,即重写其CompareTo方法,在CompareTo方法中编写比较的逻辑或者说比较的规则
class Person implements Comparable{int age;public Person(){}public Person(int age){this.age = age;}public String toString(){return "age=" + this.age;}//调用则是this.compareTo(p)public int compareTo(Person p){int age1 = this.age;int age2 = p.age;if(age1 == age2){return 0;}else if(age1 > age2){return 1;}else{return -1;}}
}
以上是重写的思路,实际代码直接优化成:
public int compareTo(Person p){return this.age-p.age;
}
//此时是按照age的数值大小升序排列
//想降序则直接return p.age-this.age;
例二:
class Vip implements Comparable{String name;int age;public Vip(){}public Vip(int age,String name){this.age = age;this.name = name;}public String toString(){return "Vip{name="+ name + ",age=" + age + "}";}public int compareTo(Vip vip){if(this.age == vip.age){//age一样,则比较name,做为字符串直接返回String类的compareToreturn this.name.compareTo(vip.name);}else{return this.age-vip.age;}}
}
TreeSet集合、TreeMap集合中,是自平衡二叉树,均采用中序遍历,即Iterator迭代器采用的是中序遍历的方式。

例:自平衡二叉树:100、200、50、60、80、120、140、130、135、180、666、40、55

分析:

总结:
从最顶端的根入手,分左右,以左为新树,找根分左右。就像循环的嵌套,内层循环完整一轮,算外层的一次循环。有子元素,子元素左根右一次,其父节点算是遍历了一个左节点。
//TreeMap集合部分源代码:final Entry getEntry(Object key) {// Offload comparator-based version for sake of performanceif (comparator != null)return getEntryUsingComparator(key);Objects.requireNonNull(key);@SuppressWarnings("unchecked")Comparable super K> k = (Comparable super K>) key;Entry p = root;while (p != null) {int cmp = k.compareTo(p.key);if (cmp < 0)p = p.left;else if (cmp > 0)p = p.right;elsereturn p;}return null;}
注意区分:
Comparable接口在java.lang包下,Comparator接口在java.util包下。
放到TreeSet或者TreeMap集合key部分的元素,要想做到排序,可选:
当比较规则不会发生改变的时候,或者当比较规则只有一个的时候,选用Comparable。当比较规则多个需要频繁切换的时候,用Comparator,规则改变时,传比较器A,再变,传比较器B
使用了比较器后创建TreeSet集合时,要将比较器通过构造方法传进去。
//写比较器
class VipComparator implements Comparator{public int compare(Vip v1,Vip v2){return v1.age-v2.age;}
}//规则有好几个的时候,再写比较器就好
class VipComparator2 implements Comparator{public int compare(Vip v1,Vip v2){return ...;}
}
//注意这里构造方法中要传入你的比较器
TreeMap vips = new TreeMap<>();//正确示范:
TreeMap vips = new TreeMap<>(new VipComparator());
vips.put(new Vip(22,"llg"),"SVIP");
vips.put(new Vip(23,"ls"),"VIP");
以上也可以通过匿名内部类实现:
//这东西满是坑,最后少个封号报错
//这里new的比较器对象,类名只能Comparator
//忘记指定泛型,重写compare方法时,传个Vip对象它不认识,只能Object
TreeMap vipMap = new TreeMap<>(new Comparator(){public int compare(Vip v1,Vip v2){return v1.age-v2.age;
}
});
java.util.Collection 集合接口
java.util.Collections 集合工具类,方便集合的操作
集合工具类方法补充:
List list = new ArrayList<>();
Collections.synchronizedList(list);
list.add("abc");
list.add("acq");
list.add("cba");//注意这里我泛型指定了String
//如果是自定义类,则要实现Comparable
Collections.sort(list);
for(String s:list){System.out.println(s);
}
Set hashSet = new HashSet<>();
List myList = new ArrayList<>(hashSet);
//也就说Set集合想排序可以转List,再排序,当然自带排序的就不用这么多此一举了
Collections.sort(myList);
集合的小提纲整理:
主要的集合类:
每种集合对象的创建、特点、添加元素、删除元素、遍历
下一篇:形容心里很温暖的成语