java集合专题Map接口及HashMap/Hashtable/Properties使用方法底层结构及源码分析
大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流
作者简介:
- CSDN java领域新星创作者blog.csdn.net/bug…
- 掘金LV3用户 juejin.cn/user/bug…
- 阿里云社区专家博主,星级博主,developer.aliyun.com/bug…
- 华为云云享专家 bbs.huaweicloud.com/bug…
@TOC
Map接口
Map接口我们主要学习其下的
HashMap/Hashtable/TreeMap/Properties
4个主要实现类
Map不同于Colletion
接口下的List和Set! Map是双列集合存放的是键值对的形式!
Key-Value
的形式进行存储
- 无序,无法保证插入顺序和取出顺序一致
- key可以为null且唯一,value可以为null
- Map中的key-value是任意引用数据类型,会封装在HashMap&Node对象中!
Map接口常用方法
public class Map_ {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
//添加元素!
map.put("李白",19);
//通过key获取value!
System.out.println("李白:"+map.get("李白"));
//删除元素!
map.remove("李白");
System.out.println("size:"+map.size());
//替换,如果需要替换的key不存在,不会添加该元素(和put的区别)
map.replace("李白",100);
System.out.println(map);
Map<String,Integer> map1 = new HashMap<>();
map.put("李白",190);
map1.put("曹操",18);
map1.put("关羽",31);
map1.put("张飞",12);
//添加map1到map中!
map.putAll(map1);
System.out.println(map);
}
}
Map遍历方式
使用下面的4种方法从而实现map遍历
- containsKey:查找key是否存在
- KeySet:获取map所有key值
- entrySet:获取所有的key-value
- values:获取所有的value
注意:这里map下的键值对是通过Node进行存储的!
这里的keySet或者 Map.Entry只是保存的索引值,只是为了遍历方便!
public class MapFor {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
for (int i = 0; i < 5; i++) {
map.put(i+"",i*i);
}
System.out.println("===1.keySet获取key后通过get获取value===");
Set<String> keyset = map.keySet();
System.out.println("1.1迭代器遍历");
Iterator<String> iterator = keyset.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.print(next+"->"+map.get(next)+" ");
}
System.out.println("\n===1.1增强for遍历===");
for ( String x:keyset) {
System.out.print(x+"->"+map.get(x)+" ");
}
//我们知道set没有get方法所以不能进行普通for遍历!
System.out.println("\n===2.通过EntrySet获取到key-value===");
//这里注意:因为entrySet的类型Map.Entry:map下的Entry存放了Node的key-value引用
Set<Map.Entry<String, Integer>> entries = map.entrySet();
System.out.println("===2.1迭代器iterator遍历===");
Iterator<Map.Entry<String, Integer>> iterator1 = entries.iterator();
while (iterator1.hasNext()){
Map.Entry<String,Integer> next = iterator1.next();
//Map.Entry类提供了 getKey 和 getValue 方法!
System.out.print(next.getKey()+"->"+next.getValue()+" ");
}
System.out.println("\n===2.2增强for遍历===");
for (Map.Entry<String,Integer> x : entries) {
System.out.print(x.getKey()+"->"+x.getValue()+" ");
}
//这里依然是得到的set集合,而set没有get方法,不能普通for遍历
System.out.println("\n===3.values方法获取所有的value值===");
//注意这里values方法返回的是Collection接口实现类
Collection<Integer> values = map.values();
System.out.println("===3.1迭代器遍历===");
Iterator<Integer> iterator2 = values.iterator();
while (iterator2.hasNext()){
Integer next = iterator2.next();
System.out.print(next+" ");
}
System.out.println("\n===3.2增强for遍历===");
for (Integer x :values) {
System.out.print(x+" ");
}
}
}
练习题
使用HashMap添加3个员工对象
要求key:员工id 值: 员工对象
遍历结果显示工资> 18000的员工
员工类:姓名,工资,员工id
public class MapExercies {
//员工类!
static class Emp{
private int id;
private String name;
private int sal;
public Emp(int id, String name, int sal) {
this.id = id;
this.name = name;
this.sal = sal;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", sal=" + sal +
'}';
}
}
public static void main(String[] args) {
Map<Integer,Emp> map = new HashMap<>();
map.put(1,new Emp(1,"刘备",16000));
map.put(4,new Emp(4,"曹操",19000));
map.put(2,new Emp(2,"吕布",1000));
//entrySet方法先获取到 key-value
Set<Map.Entry<Integer,Emp>> entries = map.entrySet();
System.out.println("=迭代器遍历=");
Iterator<Map.Entry<Integer, Emp>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<Integer,Emp> next = iterator.next();
if(next.getValue().sal>18000){
System.out.println(next.getKey()+"->"+next.getValue());
}
}
Set<Integer> keyset = map.keySet();
System.out.println("=keySet增强for遍历=");
for (Integer key : keyset) {
if(map.get(key).sal>18000){
System.out.println(key+"->"+map.get(key));
}
}
}
}
HashMap
- HashMap是Map接口中使用频率最高的类
- HashMap是以key-value的形式存储(HashMap$Node类型)
- Key值不能重复,value可以重复如果key相同,key不变,value会覆盖,允许key和value为空
- 和HashSet一样不能保证映射顺序,hash表的方式存储(jdk8底层是数组+链表+红黑树)
- HashSet没有实现线程同步,线程不安全
底层结构源码分析
重点:
扩容机制:和HashSet相同
1.HashMap底层维护了Node类型的table,默认为空
2.创建对象初始化,只是将装载因子(loadfacter)赋值为0.75
3.判断2个key相同的方式:先hash处理(调用该key的hashCode方法获取h^h>>>16),然后进行索引映射(i=(len-1)%hash),如果索引位置为空直接添加,如果非空判断引用是否相同 | | equals是否相同如果满足其一就说明key相同进行value覆盖,否则链表尾插
3.当添加第一个对象时,第一次扩容为16(threshold阀值为16*0.75)
4.以后进行扩容,超过阀值就进行2倍扩容,然后更新阀值
5.在jdk8当一条链表的长度超过8就会进行树化判断,如果当前容量(数组的长度)大于等于64就会将该链红黑树树化如果未达到数组长度未到达64就会进行2倍扩容
这里的源码分析和HashSet一样就不在进行分析了,下次一定(等忘了再来!)
Hashtable
- Hashtable类和HashMap底层相同不过是数组+链表不会树化
- Hashtable线程安全,方法添加了synchronized关键字
- Hashtable中key和value都不能为空!
- 初始化容量即为11,然后loadfactor=0.75
- 扩容方式,超过阀值就就进行扩容,2倍+1
底层结构源码分析
TreeMap
- TreeMap底层是一颗红黑树
- TreeMap可以传入比较器进行排序
- TreeMap无序集合(插入和读取不一致)
- TreeMap可以为null
可以看到TreeMap继承了AbstractMap类
AbstractMap类继承了Map接口这样就减少了TreeMap类实现Map接口的复杂性,直接继承AbstactMap类即可! 这里还继承了NavigableMap接口
而NavigableMap实现了SorteMap接口!
SortMap接口有comparator方法,所以TreeMap
类可以传入比较器进排序
public class TreeMap_ {
//通过年龄进行排序
static class Emp{
private String name;
@Override
public String toString() {
return "Emp{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Emp(String name, int age) {
this.name = name;
this.age = age;
}
private int age;
}
public static void main(String[] args) {
TreeMap<Emp,String> treeMap = new TreeMap<>(new Comparator<Emp>() {
@Override//年龄升序排列
public int compare(Emp o1, Emp o2) {
return o1.age-o2.age;
}
});
treeMap.put(new Emp("刘备",123),"1");
treeMap.put(new Emp("张飞",888),"1");
treeMap.put(new Emp("李白",5),"1");
System.out.println(treeMap);
}
}
源码分析
有点复杂红黑树的插入,所以咱们后面再回来干他!
Properties
Properties类继承了Hashtable!
他主要特点就是为了读取.properties
等资源配置配置文件用于IO操作的一个类!
- key-value都为字符串
- 虽然Hashtable的那些方法都能用,我们一般都是用该类下特有的方法
public class Properties_ {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
//保存数据
properties.setProperty("刘备","14");
properties.setProperty("李白","24");
properties.setProperty("张飞","13");
//获取数据
System.out.println("李白:"+properties.getProperty("李白"));
//将数据保存到配置文件!
OutputStream file = new FileOutputStream("db.properties",true);
properties.store(file,"注解");
System.out.println(properties);
//读数据!
Properties prop = new Properties();
prop.load(new FileReader("db.properties"));
System.out.println(prop);
}
}
- 点赞
- 收藏
- 关注作者
评论(0)