Map接口和常用方法
Map接口实现类的特点
- 1,Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
- 2,Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中,也可以说一对k-v就是一个Entry(因为Node实现了Entry接口)
-,3,Map中的key不允许重复,当有相同的k,新的v就会替换之前的v - 4,Map中的value可以重复
- 5,Map的key可以为null,value也可以为null。注意key为null,只能有一个,value为null,可以多个
- 6,常用String类作为Map的key
- 7,key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
public class Map_ {
@SuppressWarnings({"null"})
public static void main(String[] args) {
//使用实现类HashMap来解读Map实现类的特点
HashMap map = new HashMap();
map.put("no1","张三");//k-v
map.put("no2","李四");
map.put("no3","王五");
//Map中的key不允许重复,当有相同的k,新的v就会替换之前的v
map.put("no1","赵六");
//Map中的value可以重复
map.put("no4","李四");
//Map的key可以为null,value也可以为null
map.put(null,null);
//注意key为null,只能有一个,value为null,可以多个
map.put(1,null);
map.put(null,2);
//常用String类作为Map的key
map.put(new Object(), "金毛狮王");//k-v
//通过 get 方法,传入 key ,会返回对应的 value
System.out.println(map.get("no1"));
System.out.println("map="+map);
}
}
结果
- 8,Map存放数据key-value示意图,一对k-v是存放在HashMap$Node中,也可以说一对k-v就是一个Entry(因为Node实现了Entry接口)
Map接口常用方法
- 1,根据键删除映射关系 map.remove()
- 2,获取,根据键value map.get() 输入k获取对应的v
- 3,获取元素个数 map.size()
- 4,判断个数是否为0 map.isEmpty()
- 5,清除k-v map.clear();
- 6,查找键k是否存在 map.containsKey()
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超",new Book("",100));
map.put("邓超","孙俪");//替换
map.put("王宝强", "马蓉");//OK
map.put("宋喆", "马蓉");//OK
map.put("刘令博", null);//OK
map.put(null, "刘亦菲");//OK
map.put("鹿晗", "关晓彤");//OK
System.out.println("map="+map);
//1,根据键删除映射关系 map.remove()
map.remove(null);//删除k为null的对象
System.out.println("map="+map);
//2,获取,根据键value map.get() 输入k获取对应的v
System.out.println(map.get("邓超"));
//3,获取元素个数 map.size()
System.out.println(map.size());
//4,判断个数是否为0 map.isEmpty()
System.out.println(map.isEmpty());
//5,清除k-v map.clear();
map.clear();
System.out.println("清除后的map="+map);
//6,查找键k是否存在 map.containsKey()
System.out.println(map.containsKey("邓超"));//F
}
}
class Book{
private String name;
private int num;
public Book(String name, int num) {
this.name = name;
this.num = num;
}
}
结果
Map接口遍历方法
首先:containKey 查找键是否存在
-方式1:先取出所有的键,然后根据键取出对应的值
- 方式2,通过调用values方法来把所有的value取出
- 方式3,通过EntrySet来获取k-v
public class MapMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超","孙俪");//OK
map.put("王宝强", "马蓉");//OK
map.put("宋喆", "马蓉");//OK
map.put("刘令博", null);//OK
map.put(null, "刘亦菲");//OK
map.put("鹿晗", "关晓彤");//OK
//containKey:查找键是否存在
System.out.println(map.containsKey("邓超"));//T
//方式1,先取出所有的键,然后根据键取出对应的值
Set keyset = map.keySet();
System.out.println("=======迭代器=======");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key+"-"+map.get(key));
}
System.out.println("=========增强for========");
for(Object o:keyset){
System.out.println(o+"-"+map.get(o));
}
//方式2,通过调用values方法来把所有的value取出
Collection values = map.values();
System.out.println("=======取出所有value的迭代器=======");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object value = iterator1.next();
System.out.println(value);
}
System.out.println("===========取出所有value的增强for===========");
for (Object v:values){
System.out.println(v);
}
//方式3,通过EntrySet来获取k-v
Set entrySet = map.entrySet();
System.out.println("======使用EntrySet迭代器=========");
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object entry = iterator2.next();
// System.out.println(entry.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型-Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue());
}
}
}
结果
Map接口的常用实现类:HashMap、Hashable、Properties
Map接口实现类-HashMap
- HashMap是Map接口使用频率最高的实现类
- HashMap是以key-val对 的方式来存储数据(HashMap$Node类型)
- key不能重复,但是value值可以重复,允许使用null键和null值
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,val会替换)
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的
- HashMap没有实现同步,因此线程是不安全的,方法没有做同步互斥,没有synchronized
HashMap的扩容机制
- 1,HashMap底层维护了Node类型的数组table,默认为null
- 2,当创建对象时,将加载因子(loadfactor)初始化为0.75
执行构造器 new HashMap()
初始化加载因子 loadfactor = 0.75
HashMap$Node[] table = null - 3,当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key是否相等,如果相等就直接替换val;如果不相等需要判断是树结构还是链表结果,做出相应处理。如果添加时发现容量不够,则需要扩容
- 4,第一次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)
- 5,以后再扩容,则需要扩容table容量为原来的两倍(32),临界值为原来的两倍,即24,依次类推
- 6,在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
Map接口实现类-Hashtable
Hashtable的基本介绍
- 存放的元素思键值对:即K-V
- Hashtable的键和值都不能为null,否则会抛出NullPointerException
- key不能重复,但是value值可以重复
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,val会替换)
- Hashtable使用的方法基本和HashMap一样
- Hashtable是线程安全的(synchronized),HashMap是线程不安全的
Hashtable和HashMap对比
Map接口实现类-Properties
基本介绍
- Properties类继承自Hashtable类并实现了Map接口,也是使用一种键值对的形式来保存数据
- 它的使用特点和Hashtable类似
- Properties还可以从xxx.properties文件中,加载数据到properties类对象,并进行读取和修改
public class Properties_ {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Properties properties = new Properties();
// properties.put(null,null);//抛出 空指针异常
properties.put("john", 100);//k-v
properties.put("lucy", 100);
properties.put("lic", 100);
properties.put("lic", 88);//如果有相同的 key , value 被替换
System.out.println(properties);
//通过 k 获取对应值
System.out.println(properties.get("lic"));
//删除
System.out.println(properties.remove("lic"));
//修改
properties.put("john","乔治");
System.out.println("john的v修改后"+properties);
}
}
结果
总结-开发中如何选择结合实现类(记住)
开发中如何选择结合实现类,主要取决于 业务操作特点,然后根据集合实现类特性进行选择
- 先判断存储的类型
一组对象[单列]:Collection接口
允许重复:List
增删多:LinkedList【底层维护了一个双向链表】
改查多:ArrayList【底层维护了 Object类型的可变数组】
不允许重复:Set
无序:HashSet【底层是HashMap,维护了一个哈希表】
排序:TreeSet
插入和取出顺序一致:LinkedHashSet,维护数组+双向链表
一组键值对[双列]:Map
键无序:HashMap【底层是:哈希表 jdk7:数组+链表 jdk8:数组+链表+红黑树】
键排序:TreeMap
键插入和取出一致:LinkedHashMap
读取文件:Prpperities
TreeSet实现排序
使用TreeSet提供的一个构造器,传入一个比较器(匿名内部类new Comparator()) 来制定顺序
public class TreeSet_ {
@SuppressWarnings({"all"})
public static void main(String[] args) {
//调用无参构造器,元素仍然是无序的
TreeSet treeSet = new TreeSet();
treeSet.add("jack");
treeSet.add("tom");
treeSet.add("zl");
treeSet.add("aa");
// System.out.println("未定序的treeSet="+treeSet);//无序
//使用TreeSet提供的一个构造器,传入一个比较器(匿名内部类) 来制定顺序
TreeSet treeSet1 = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// 调用String 的compareTo方法进行字符串大小比较
// return ((String)o1).compareTo((String)o2);
//调用String 的 length方法,进行长度大小的比较
return ((String)o1).length()-((String)o2).length();
}
});
treeSet1.add("jack");
treeSet1.add("tom");
treeSet1.add("zl");
treeSet1.add("a");
System.out.println(treeSet1);
}
}
结果
TreeMap实现排序
使用TreeMap提供的一个构造器,传入一个比较器(匿名内部类new Comparator()) 来制定顺序
public class TreeMap_ {
@SuppressWarnings({"all"})
public static void main(String[] args) {
//调用无参构造器,元素仍然是无序的
TreeMap treeMap = new TreeMap();
treeMap.put("jack", "杰克");
treeMap.put("tom", "汤姆");
treeMap.put("kristina", "克瑞斯提诺");
treeMap.put("smith", "斯密斯");
System.out.println(treeMap);
TreeMap treeMap1 = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//下面 调用 String 的 compareTo 方法进行字符串大小比较
// return ((String)o2).compareTo(((String)o1));
//调用String 的 length方法,进行长度大小的比较
return ((String)o2).length()-((String)o1).length();
}
});
treeMap1.put("jack", "杰克");
treeMap1.put("tom", "汤姆");
treeMap1.put("kristina", "克瑞斯提诺");
treeMap1.put("smith", "斯密斯");
System.out.println(treeMap1);
}
}
结果