1、Map<k,v>接口的常用方法
V put(K key, V value) 添加键值对
void clear() 清除所有键值对
boolean containsKey(Object key) 是否包含指定键 是就返回true
boolean containsValue(Object value) 是否包含指定值 是就返回true
V get(Object key) 以键找值 返回值
boolean isEmpty() 此地图是否包含键值对 不包含返回true
Set<K> keySet() 获取所有的键 以set集合形式返回
V remove(Object key) 以键删除键值对
int size() 此地图中含有多少个键值对
Set<Map.Entry<K,V>> entrySet() 把Map转换成Set集合
Map: 1:zhangsan 2:lisi 3:wangwu
Set: 1=zhangsan 2=lisi 3=wangwu [它们的类型是Map.Entry<k,v>]
public static void main(String[] args) { //创建Map集合对象 Map<Integer,String> map=new HashMap<>(); //添加键值对 map.put(1,"zhangsan"); map.put(2,"wangwu"); map.put(3,"lisi"); map.put(4,"liuliu"); //Map中键值对的个数 System.out.println(map.size());//4 //以键找值 System.out.println(map.get(1));//zhangsan //包含键 System.out.println(map.containsKey(3));//true //包含值 System.out.println(map.containsValue("zhangsan"));//true //删除键值对 String v=map.remove(2); System.out.println(v);//wangwu //是否为空 System.out.println(map.isEmpty());//false //返回所有键 Set<Integer> ints = map.keySet(); for (Integer anInt : ints) { System.out.print(anInt+" ");//1 3 4 }
2、有关Map取键值对
键值对的第一种获取方式:先获取所有的键,在获取值
第二种方式 把键值对一起取出来(效率较高)
public static void main(String[] args) { Map<Integer,String> map=new HashMap<>(); map.put(1,"wo"); map.put(2,"hao"); map.put(3,"ni"); //键值对的第一种获取方式:先获取所有的键,在获取值 //获取所有键 Set<Integer> keys = map.keySet(); /* //迭代器 Iterator<Integer> k= keys.iterator(); //迭代 while(k.hasNext()){ Integer k1 = k.next(); System.out.println(k1+":"+map.get(k1)); } */ //foreach for (Integer key : keys) { System.out.print(key+":"+map.get(key)+" ");//1:wo 2:hao 3:ni } System.out.println(); //第二种方式 把键值对一起取出来 Set<Map.Entry<Integer, String>> set = map.entrySet(); for (Map.Entry<Integer, String> node: set) { System.out.print(node.getKey()+":"+node.getValue()+" ");//1:wo 2:hao 3:ni } }
3、HashMap
1>、HsahMap集合底层是哈希表/散列表的数据结构
2>、哈希表是一个数组和单向列表的结合体
数组在查询方面效率很高,链表在增删方面效率很高
3>、HsahMap底层源代码
//HashMap底层实际上是一个一维数组 Node<k,v>[] table; //静态内部类HashMap.Node static class Node<K,V> implements Map.Entry<K,V> { final int hash;//哈希值,是key的hashCode()方法执行结果。hash值通过算法函数,可以转换成地址 final K key;//存储到Map集合中的key V value;//存储到Map集合中的Value Node<K,V> next;//下一个节点的内存地址 }
哈希表/散列表:一维数组,这个数组中的每一个元素都是一个单向链表
4、HashMap集合的存取原理
Map中的put(k,v)方法原理:
接收k,v后,封装到Node<k,v>对象中,调用k的hashCode()方法,得到K的哈希值,通过哈希函数与算法生成数组下标值,找到对应的数组下标位置,若为空,则直接把键值对放进去,若不为空,则一一比较在此数组下标 位置上的链表的k值,使用equals,返回true则把v值用新的覆盖,返回false,则把此键值对放在链表的末尾
Map中的get(k)方法原理:
接收到k值后 底层调用hashCode方法,得到k的哈希值后通过哈希函数和算法生成数组下标,通过数组下标快速定位到链表在数组中的位置,若此位置为null则返回null,若不为空,拿k与单链表一一比较通过equals ,返回true则拿出k对应的v值,返回false 则返回null。
综上知,若要自定义引用类型,使用集合,一定要重写equals和hashCode方法。
如果hashCode返回的值只有一个,则此哈希表为纯链表,属于不均匀散列表
分布均匀散列表:有100个元素,数组空间为10,则每个空间有10个;
如果hashCode返回值每次都返回不一样的值,则哈希表为纯数组,属于不均匀散列表
重点:HashMap集合的默认初始化容量是16,默认因子是0.75 默认因子:当HashMap底层数组的容量达到75%的时候,数组开始扩 容,HashMap 集合的初始化容量必须是2的倍数,因为为了达到散列 均匀,调高HashMap集合的存取效率
如图所示
(图为B站老师所画)
HashMap集合的key特点
无序不可重复:
无序:因为不一定挂到那个链表上
不可重复:equals保证了key不会重复
*JDK8之后在哈希表中当单向链表超过8个,则数据结构会变为红黑树结构,当红黑树节点小于6时又变回链表
5、Hashtable
Hashtable可以为空吗?
Hashtable的键与值都不可以为null
HashMap的键与值可以为null
Hashtable方法都带有synchronized:线程安全的。线程安全有其他方案,这个Hashtable对线程的处理效率较低,运用较少了。
底层都是哈希表,初始化容量是11,默认加载容量0.75f,扩容是原容量乘以2加1.
public static void main(String[] args) { Map map=new Hashtable(); map.put(null,100);//NullPointerException异常 System.out.println(map.size()); }
6、Properties
Properties属性类对象的相关方法:
Properties是Map的一个集合,继承Hashtable
Properties的key与value都是String类型的
Properties被称为属性类对象
//存 properties.setProperty("url","jdbc:mysql://localhost:3306/product"); //取 String url = properties.getProperty("url"); System.out.println(url);//jdbc:mysql://localhost:3306/product
7、TreeSet
1>、TreeSet集合无序不重复,但可以按大小排序,底层是TreeMap
2>、TreeMap的底层是二叉树
3>、TreeSet存储数据相当于在TreeMap的key里存储
4>对于自定义类型来说,可以排序吗?
无法排序,因为没有指定排序规则 会出现ClassCastException异常:class EighteenDay.student cannot be cast to class java.lang.Comparable
在底层代码中,HashMap的put方法会进行键的比较(compareTo()方法),键值在比较前会强制转换成Comparable比较器类型,而student为自定义类型 没有实现比较器接口,故无 法转型,报错。
总结:在使用TreeSet/TreeMap集合存储自定义类型数据时,自定义类一定要继承Java.lang.Comparable,重写compareTo方法进行排序规则的制定
public static void main(String[] args) {
student st=new student(13);
student st2=new student(12);
TreeSet<student> ts=new TreeSet<>();
ts.add(st);
ts.add(st2);
System.out.println(ts.size());
for (student t : ts) {
System.out.println(t);
}
}
class student implements Comparable<student>{
int age;
public student(int age) {
this.age = age;
}
//重写compareTo 制定排序规则
@Override
public int compareTo(student o) {
return this.age-o.age;//这个是降序 o.age-this.age 就顺序相反了(升序)
/*底层代码以二叉树的形式排序,我们需要在这里重写compareTo返回三种情况的某一种,负数,0,正数
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
*/
}
//重写toString方法
@Override
public String toString() {
return "age=" + age;
}
}