我们来查看Map接口:
里面的k-v和set集合的不同之处在于:key仍然是一个对象,但是对于set来说value是一个常量,
private static final Object PRESENT = new Object();
set里面的value放的是 静态性质的PRESENT。
而map的value是自己传进去的。map接口是一个双列集合,set接口是一个单列集合。
Map接口实现类的特点:
(1)Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value(双列元素)
(2)Map中的Key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
我们发现table表是HashMap$Node这样子的一个数组。
(3)Map中的key不允许重复,原因和hashSet一样。(无序)
(4)Map中的value可以重复(无序)
(5)Map的key可以为Null,value也可以为null,注意key为null,只能有一个,value为null,可以多个。但是HashMap可以存储,HashTable不可以存储。
package com.rgf.jihe;
import org.junit.Test;
import java.util.HashMap;
import java.util.Hashtable;
/**
* Map:双列数据,存储key-value对的数据 --类似于高中的函数:y=f(x)
* HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value.
* LinkedHashMap
* TreeMap:
* Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value.
* Properties:
*/
public class MapTest {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put(null,123);
map.put(null,null);
Hashtable hashtable = new Hashtable();
hashtable.put(null,null);
}
}
运行之后如下所示:
所以HashMap可以为null,但是Hashtable不可以为null。
(6)常用string类作为Map的key,而只要object的子类都可以作为key。
(7)key和value之间存在单向一对一关系,即通过指定的Key总能找到对应的value.
我们的代码设计如下所示:
package com.rgf.map;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"all"})
public class Map_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1","rgf");//k-v
map.put("no2","张无忌");
map.put("no1","张三丰");//当有相同的key值时,会替换掉上面的value
map.put("no3","张三丰");
map.put(null,null);
map.put(null,"abc");//等价替换
map.put("no4",null);//k-v
map.put("not5",null);//k-v
map.put(1,"赵敏");//k-v
map.put(new Object(),"金毛狮王");//k-v
//通过get方法,传入key,会返回对应的value
System.out.println(map.get(1));
System.out.println("map="+map);
}
}
运行界面如下所示:
(8)Map存放数据的key-value示意图,一对k-v是放在一个HashMap$Node中,又因为Node实现了Entry接口,有些书上也说一对k-v就是一个Entry。(Entry里面有两个属性:一个key,一个values,无序不可重复的)(key放在set集合里面,values放在Collection接口下面的实现子类里面的)但是,真正的Key是放在HashMap$Node这个类型里面的。而我们的set和collection集合只是指向了他而已。就是简单的引用。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
其中newNode返回了hashMap里面的Node类型。
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
Node为HashMap里面的静态内部类:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
其中Node也实现了Entry接口
Entry这个集合中包含2类,一类是Set型的KeySet,用来存放key值,另外一类是存放value值,然后他们里面各自的值其实存放的是地址,一个指向真正Node节点中的key和value的地址。而真正存放Key和value的值是HashMap$Node。
我们来通过源码进行较深入的理解:
1.k-v最后是存放在HashMap$Node node=new Node<>(hash, key, value, next)这个类型里面的 2.k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型就是Entry, // 而一个Entry对象就包含了key,value,EntrySet<Entry<K,V>>,即:transient Set<Map.Entry<K,V>> entrySet() ,有一个EntrySet集合,EntrySet集合里面放的是Entry这样子的数据类型,Entry这样子的数据类型包含key和value;
3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,Node实现了Map.Entry接口,这是因为HashMap$Node implements Map.Entrty,HashMap$Node实现了这个接口Map.Entrty。 当有一个类,实现了一个接口,这个类的对象实例就可以赋给我们的接口类型。
我们进行总结就是EntrySet内部类存储了Node节点的引用,返回一个集合,方便遍历。
我们进入如下源码所示:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
(4)这样当把HashMap$Node对象存放到entrySet方便我们的遍历,因为Map.Entry提供了两个非常重要的方法
interface Entry<K,V> {
/**
* Returns the key corresponding to this entry.
*
* @return the key corresponding to this entry
* @throws IllegalStateException implementations may, but are not
* required to, throw this exception if the entry has been
* removed from the backing map.
*/
K getKey();
/**
* Returns the value corresponding to this entry. If the mapping
* has been removed from the backing map (by the iterator's
* <tt>remove</tt> operation), the results of this call are undefined.
*
* @return the value corresponding to this entry
* @throws IllegalStateException implementations may, but are not
* required to, throw this exception if the entry has been
* removed from the backing map.
*/
V getValue();
我们设计的代码如下所示:
package com.rgf.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@SuppressWarnings({"all"})
public class MapSource_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1","沃尔"); //k-v
map.put("no2","张无忌"); //k-v
//1.k-v最后是存放在HashMap$Node node=new Node<>(hash, key, value, next)这个类型里面的
//2.k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型就是Entry,
// 而一个Entry对象就包含了key,value,EntrySet<Entry<K,V>>,即:transient Set<Map.Entry<K,V>> entrySet()
//有一个EntrySet集合,EntrySet集合里面放的是Entry这样子的数据类型,Entry这样子的数据类型包含key和value;
//3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,Node实现了Map.Entry接口
//这是因为HashMap$Node implements Map.Entrty,HashMap$Node实现了这个接口Map.Entrty。
//当有一个类,实现了一个接口,这个类的对象实例就可以赋给我们的接口类型。
//4.这样当把HashMap$Node对象存放到entrySet方便我们的遍历,因为Map.Entry提供了两个非常重要的方法
//getKey()和getValue()方法
Set set = map.entrySet();
System.out.println(set.getClass());//输出EntrySet类型
for (Object obj :set) {
//System.out.println(obj.getClass()); //输出HashMap$Node类型
//为了从HashMap$Node取出k-v,先做一个向下转型
// System.out.println("==============");
Map.Entry entry=(Map.Entry) obj;
System.out.println(entry.getKey()+"-"+entry.getValue());
}
}
}
运行界面如下所示:
我们进行Debug来进行更好的理解:
我们entrySet里面的no2和no1其实是指向table表里面的那两个结点。
我们发现这两个表的位置是相同的。
我们这里有一个table表,是以数组+链表+红黑树的来组织我们这个Node,但是为了方便管理,我们在底层做了一个控制,我们把每一个Node封装成一个entry,然后把多个entry放到entrySet这个集合里面去,便于管理,除此之外,还提供了一个叫keyset,如果我们想指向得到这个keyset的话,可以直接把key里面的对象封装到set这样子一个集合。通过这样可以单独的取出key里面的对象。
我们继续进行设计代码:
package com.rgf.map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@SuppressWarnings({"all"})
public class MapSource_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1","沃尔"); //k-v
map.put("no2","张无忌"); //k-v
//1.k-v最后是存放在HashMap$Node node=new Node<>(hash, key, value, next)这个类型里面的
//2.k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型就是Entry,
// 而一个Entry对象就包含了key,value,EntrySet<Entry<K,V>>,即:transient Set<Map.Entry<K,V>> entrySet()
//有一个EntrySet集合,EntrySet集合里面放的是Entry这样子的数据类型,Entry这样子的数据类型包含key和value;
//3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node,Node实现了Map.Entry接口
//这是因为HashMap$Node implements Map.Entrty,HashMap$Node实现了这个接口Map.Entrty。
//当有一个类,实现了一个接口,这个类的对象实例就可以赋给我们的接口类型。
//4.这样当把HashMap$Node对象存放到entrySet方便我们的遍历,因为Map.Entry提供了两个非常重要的方法
//getKey()和getValue()方法
Set set = map.entrySet();
System.out.println(set.getClass());//输出EntrySet类型
for (Object obj :set) {
//System.out.println(obj.getClass()); //输出HashMap$Node类型
//为了从HashMap$Node取出k-v,先做一个向下转型
// System.out.println("==============");
Map.Entry entry=(Map.Entry) obj;
System.out.println(entry.getKey()+"-"+entry.getValue());
}
Set set1 = map.keySet();
System.out.println(set1.getClass());
for (Object o :set1) {
System.out.println("key="+o);
}
Collection values = map.values();
for (Object o1 :values) {
System.out.println("values="+o1);
}
System.out.println(values.getClass());
}
}
运行界面如下所示:
我们可以查看values的源码:
final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<V> iterator() { return new ValueIterator(); }
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super V> action) {
也可以查看KeySet的源码:
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Map接口和常用方法:
Map体系的继承图:
Map接口常用方法:
我们所设计的代码如下所示:
package com.rgf.map;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"all"})
public class MapMethod {
public static void main(String[] args) {
//演示map接口常用方法
Map map = new HashMap();
map.put("邓超",new Book("",100));
map.put("邓超","孙俪");//替换
map.put("王宝强","马蓉");
map.put("宋喆","马蓉");
map.put("沃尔",null);
map.put(null,"刘亦菲");
map.put("鹿晗","关晓彤");
System.out.println("map="+map);
//remove:根据键删除映射关系,里面放的是key,是根据key来进行删除的。
map.remove(null);
System.out.println("map="+map);
//get:根据键获取值
Object val = map.get("鹿晗");
System.out.println("val="+val);
//size:获取元素个数
System.out.println("k-v="+map.size());
//isEmpty:判断个数是否为0
System.out.println(map.isEmpty());//F
//clear:清除k-v
map.clear();
System.out.println("map="+map);
map.put("rgf","ypl");
//containsKey:查找键是否存在
System.out.println("结果="+map.containsKey("rgf"));//F
}
}
class Book{
private String name;
private int num;
public Book(String name, int num) {
this.name = name;
this.num = num;
}
}
运行界面如下所示:
其他补充方法如下所示:
package com.rgf.jihe;
import java.util.HashMap;
public class MapMethod {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("AA",123);
hashMap.put("BB",456);
System.out.println(hashMap);
HashMap hashMap1 = new HashMap();
hashMap1.put("CC",888);
hashMap1.put("DD",888);
//putAll:将m中的所有key-value对存放到当前map中
hashMap.putAll(hashMap1);
System.out.println(hashMap);
//containsValue:是否包含指定的value.
boolean b = hashMap.containsValue(888);
System.out.println(b);
//equals:判断当前map和参数对象obj是否相等
boolean equals = hashMap.equals(hashMap1);
System.out.println(equals);
}
}
运行之后如下所示:
Map接口遍历方法:
(1)containsKey:查找键是否存在
(2)KeySet:获取所有的键
(3)entrySet:获取所有关系k-v
(4)values:获取所有的值
我们设计代码如下所示:
package com.rgf.map;
import java.util.*;
@SuppressWarnings({"all"})
public class MapFor {
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超","孙俪");//替换
map.put("王宝强","马蓉");
map.put("宋喆","马蓉");
map.put("沃尔",null);
map.put(null,"刘亦菲");
map.put("鹿晗","关晓彤");
//第一组:先取出所有的Key,通过key取出对应的Value(先遍历所有的key)
Set keyset = map.keySet();
//(1)增强for循环
System.out.println("--------keyset第一种方式:增强for循环-------");
for (Object key :keyset) {
System.out.println(key+"-"+map.get(key));
}
//(2)iterator迭代器
System.out.println("-------keyset第二种方式:迭代器-------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key+"-"+map.get(key));
}
//第二组:把所有的values取出(先遍历所有的values)
Collection values = map.values();
//这里可以使用collection使用的遍历方法
//(1)增强for循环
System.out.println("-------values第一种方式:增强for循环-----");
for (Object value :values) {
System.out.println("values="+value);
}
//(2)iterator迭代器遍历
System.out.println("-----values第二种方式:iterator迭代器遍历");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object value = iterator1.next();
System.out.println("values=" + value);
}
//第三组:通过EntrySet来获取k-v(遍历所有的key-values)
Set entrySet = map.entrySet(); //EntrySet<Entry<K,V>>
//(1)增强for
System.out.println("-------entrySet增强for循环----");
for (Object entry :entrySet) {
//将entry转成Map.Entry
Map.Entry m =(Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue());
}
//(2)迭代器
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)
//向下转型,应该转成HashMap$Node,但是因为他没有提供相应的方法,就直接转成Map.Entry
//entrySet集合中的元素都是entry
Map.Entry m=(Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue());
}
}
}
运行界面如下所示:
我们的数据在后台生成以后,再以json这个字符串的这种方式来呈现到浏览器当中。用户想要读取数据的时候,从数据库里读取到map,map再一个一个根据转换为json,json在界面呈现。
实例练习:
使用HashMap添加三个员工对象,要求:
键:员工id
值:员工对象
并遍历显示工资>18000的员工(遍历方式至少两种)
员工类:姓名、工资、员工id
我们设计的代码如下所示:
package com.rgf.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@SuppressWarnings({"all"})
public class MapExercise {
public static void main(String[] args) {
Map map = new HashMap();
Object rgf = map.put(1, new staff("rgf", 50000, 1));
Object ypl = map.put(2, new staff("ypl", 60000, 2));
Object love = map.put(3, new staff("love", 9999, 3));
Set keySet = map.keySet();
//第一种遍历:Keyset方式:增强for循环
System.out.println("=====第一种遍历方式:keyset增强for循环=====");
for (Object key : keySet) {
//先获取value
staff emp = (staff) map.get(key);
if (emp.getWages() > 18000) {
System.out.println(emp);
}
}
System.out.println("=====第二种遍历方式:entryset迭代器方式");
//第二种遍历:entryset:迭代器方式
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object entry = iterator.next();
Map.Entry entry1 = (Map.Entry) entry;
staff value = (staff) entry1.getValue();
if (value.getWages() > 18000) {
System.out.println(value);
}
}
}
static class staff {
private String name;
private int wages;
private int id;
public staff(String name, int wages,int id) {
this.name = name;
this.wages = wages;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getWages() {
return wages;
}
public void setWages(int wages) {
this.wages = wages;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "staff{" +
"name='" + name + '\'' +
", wages=" + wages +
", id=" + id +
'}';
}
}
}
运行界面如下所示: