Map集合
这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
map的集合形式是键值对的方式;
其常用方法:
| Modifier and Type | Method | Description |
|---|---|---|
V | get(Object key) | Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. |
V | put(K key, V value) | Associates the specified value with the specified key in this map (optional operation). |
static <K,V> Map<K,V> | of() | Returns an unmodifiable map containing zero mappings. |
其中Map.of()可以将每一组数据转为map进行保存;
使用Map保存Key-Value数据:
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class 保存Key_Value数据 {
public static void main(String[] args) {
// 如果Key重复 则会报错:java.lang.IllegalArgumentException
// 如果Value与Key设置为null,则会报错:java.lang.NullPointerException
Map<String,Integer> map = Map.of("one",1,"two",null);
System.out.println(map);
// System.out.println(map.get("one"));
// Put只能在map子类中使用
}
}
注意点:
- 如果Key重复 则会报错:java.lang.IllegalArgumentException
- 如果Value与Key设置为null,则会报错:java.lang.NullPointerException
HashMap子类:
特点:采用散列方式保存数据,即无序排列
继承结构:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
HashMap进行map集合的操作:
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
public class HahMap子类 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
// 进行Map的集合操作
// map.put("One",1);
// map.put("Two",2);
// map.put("Three",3);
// System.out.println(map);
// Map数据的保存方法:
System.out.println(map.put("One",1)); //保存数据,但是保存的数据的key不存在,返回null
System.out.println(map.put("One",101)); // 返回1,也就是返回的是覆盖掉的值
map.put("Three",3);
map.put("demo1",4);
map.put("demo2",null);
map.put("Te",6);
map.put(null,7);
/*结果
* null、7
* */
System.out.println(map.get("demo2"));
System.out.println(map.get(null));
}
}
注意两个输出的注释:
//Map数据的保存方法:
System.out.println(map.put("One",1)); //保存数据,但是保存的数据的key不存在,返回null
System.out.println(map.put("One",101)); // 返回1,也就是返回的是覆盖掉的值
在使用Map保存数据的时候Key与Value都不能使用null,但是使用HashMap进行保存数据可以将Key或Value设置为null,当然也可以Key=Value=null,但是这样的实现保存毫无意义。
put方法在发生覆盖钱都可以返回原始内容,这样就可以依据返回结果来判断所设置的key是否存在;
HashMap数据扩充操作原理分析:
1) 首先观察构造方法:
设置数据扩充阈值;
public HashMap() {
// all other fields defaulted
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
然后跳转DEFAULT_LOAD_FACTOR查看:
作用:容量的扩充阈值
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
通过源码可以发现,每个Hashmap在对象实例化的时候都已经考虑到了数据存储的扩充问题;
2) 观察HashMap中的put方法
public V put(K key,V value){
return putVal(hash(key),key,value,false,true);
}
在使用put方法进行数据保存的时候会调用putVal方法,同时会将key进行哈希处理(生成hash码)
而putVal()方法为了方便数据保存会将数据封装为一个Node节点类对象,而在使用putVal()方法的操作过程会调用reasize()方法进行扩容;
3)容量扩充
当保存的数据超过既定的存储容量则会进行扩容,原则如下:
常量地址:DEFAULT_INITIAL_CAPACITY;作为初始化的容量配置,而后1向左移4为-》16;
常量的默认大小为16个元素,也就是说默认可以保存的最大的内容是16;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
当保存的数据内容超过了设置的阈值DEFAULT_LOAD_FACTOR = 0.75f
相当于容量x阈值 = 16*0.75 = 12;即保存到12个元素的时候就会进行容量扩充;
扩充的模式是2倍扩充:即每一次扩充2倍的容量。
4)大数据下的数据存储方式:
在JDK1.8之后来到大数据时代,这就触发了HashMap在大数据量中的访问效率问题;
其中提供了一个重要的常量:TREEIFY_THRESHOLD
static final int TREEIFY_THRESHOLD = 8;
在使用HashMap进行保存的时候,如果保存的数据个数没有超过阈值8,那么会按照链表的形式进行数据的存储;而超过了这个阈值,则会将链表转为红黑树以实现树的平衡;并且利用左旋与右旋保证数据的查询性能。
LinkedHashMap子类:
特点:基于链表形式实现偶对的存储,可以保证存储顺序与数据增加的顺序相同;
继承结构:
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
使用LinkedHashMao子类存储数据:
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子类存储数据 {
public static void main(String[] args) {
Map<String,Integer> map = new LinkedHashMap<String,Integer>();
map.put("张三",1);
map.put("李四",2);
map.put("王五",3);
map.put(null,3);
map.put("赵六",null);
System.out.println(map);
}
}
运行可以发现集合的保存的顺序与数据增加顺序相同;同时LinkedHashMap子类允许保存的Key或value内容为null;
Hashtable子类:
其继承结构如下:
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable
使用Hashtable子类保存数据:
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子类存储数据 {
public static void main(String[] args) {
Map<String,Integer> map = new Hashtable<String,Integer>();
map.put("张三",1);
map.put("李四",2);
System.out.println(map);
}
}
HashMap与Hashtable的区别:
HashMap中的 方法都是异步操作(非线程安全),HashMap中允许保存null数据
Hashtable中的方法都是同步操作(线程安全),但是效率慢,Hashtable不允许保存Null数据;否则会出现NUllpointException;
TreeMap子类:
特点:TreeMap属于有序的Map集合类型;它可以按照key进行排序;所以需要Comaprable接口配合;
继承结构如下:
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
TreeMap子类进行数据Key的排序:
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.Map;
import java.util.TreeMap;
public class TreeMap子类进行数据Key的排序 {
public static void main(String[] args) {
// 因为该程序保存的Key属于String类型,由于String中实现了Comparable接口,所以
// 可以根据保存的字符的编码进行由低到高的进行排序
/*
* public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
*
*
* */
Map<String,Integer> map = new TreeMap<String,Integer>();
map.put("C",3);
map.put("B",2);
map.put("A",1);
System.out.println(map);
}
}
Map.Entry内部接口:
在JDK1.9开始可以利用Map接口中创建Map.entry内部接口实例;
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.Map;
public class Map_Entry内部接口 {
public static void main(String[] args) {
Map.Entry<String,Integer> entry = Map.entry("One",1);
System.out.println(entry.getKey());
System.out.println(entry.getValue());
//观察使用的子类
System.out.println(entry.getClass().getName());
}
}
在程序继续宁Map.Entry对象构建的时,只传入Key和Value就会自动利用KeyValueHolder子类实例化Map.Entry接口对象。
Iterator输出Map集合:
集合数据输出的标准形式是基于Iterator接口完成的;Collection接口直接提供iterator方法可以获得iterator接口实例;但由于Map接口中保存的数据是多个Map.Entry接口封装的二元偶对象,所以就必须采用Map集合的迭代输出;
- 使得Map接口中的entrySet(),将Map集合变为Set集合;
- 取得Set接口实例后就可以利用iterator方法取得iterator的实例化对象;
- 使用iterator迭代找到每一个Map.Entry对象,并进行Key与Value的分。
Iterator和foreach输出Map集合:
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Map集合的输出问题 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("One",1);
map.put("two",2);
// 输出的map中的Key与Value的值
Set<Map.Entry<String, Integer>> set = map.entrySet();//将Map集合转变为Set集合
Iterator<Map.Entry<String,Integer>> iter = set.iterator(); //获取Iterator接口
while(iter.hasNext()){
// Set中存的都是Map.Entry()对象
Map.Entry<String, Integer> me = iter.next();
System.out.print(me.getKey()+" "+me.getValue());
}
// System.out.println("\n");
//foreach循环输出Map集合:
for(Map.Entry<String,Integer> entry:set){
System.out.print(entry.getKey()+" "+entry.getValue());
}
}
}
自定义Key类型:
采用自定义类的形式实现,但是作为Key类型的类由于存在数据查找的需求,所以必须在类中覆写hashcode()与equals()方法。
package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
class Member{
private String name;
private int age;
public Member(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Member)) return false;
Member member = (Member) o;
if (age != member.age) return false;
if (name != null ? !name.equals(member.name) : member.name != null) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
/*
* Key进行自定义;作为Key类型的类由于存在数据查找的需求,所以应该在类中覆写HashCode() 和equals();
* */
public class 自定义Key的值 {
public static void main(String[] args) {
Map<Member,String> map = new HashMap<Member,String>();
map.put(new Member("张三",22),"xbhog");
map.put(new Member("李四",23),"博客");
map.put(new Member("王五",26),"welcome");
System.out.println(map.get(new Member("张三",22)));
}
}
在存储大量的数据中,key还是可能出现重复的问题,这个问题叫Hash冲突;
解决的方式:
链地址法(拉链法)、开放寻址、再哈希、建立公共溢出区;
结束:
如果你看到这里或者正好对你有所帮助,希望能点个👍或者⭐感谢;
有错误的地方,欢迎在评论指出,作者看到会进行修改。