Map集合之实现类HashMap&Hashtable&TreeMap&LinkedHashMap&Properties浅析

在我们生活中,集合的概念就已经不陌生,比如身份证号对应的就一个人、电脑IP地址对应的就是主机名、学号对应就是一个学生等,其实就是一种一一对应的关系,我们可以认为这就是映射.在Java中就提供了专门的集合类用来存放这种对象(映射)关系的对象,即java.util.Map<K,V>接口>。面向对象大多的思想其实很生活化,只是转成计算机的语言、思想,这需要我们细细琢磨、研究!

1.Collection与Map接口的区别

Collection的集合中的元素是独立的>,如同单身狗,向集合中存储数据的时候是一个个元素进行存储。

Map的集合中的元素是成对的>,如同情侣,向集合中存储数据的时候是由键和值组成的元素的进行存储,其中通过键可以查找到对应的值;Map中的集合不能包含重复的键,值可以是重复的;每一个键只能对应一个值(可以是单个值、集合、数组值)。

总结:Collection的集合可以成为单列集合,Map的集合可以成为双列集合

2.Map的常用方法

①添加

(1)V put(K key,V value): 将指定的值与此映射中的指定键关联(可选操作)。

演示代码1:

/*存储一个小组的选手的信息,key是编号,value是姓名*/
public class MapTest01 {
	@Test
	public void Test01() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		Map<Integer,String> map2 = new HashMap<>();
		map2.put(3, "大白");
		map2.put(4, "小白");
		System.out.println("map1的映射关系对数:"+map1.size());//map1的映射关系对数:2
		System.out.println("map2的映射关系对数:"+map2.size());//map2的映射关系对数:2

	}
}

演示代码2:

	@Test
    /*注意:在原有的映射关系上,根据key值进行put操作value值,实质上就是替换操作*/
	public void Test02() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		System.out.println("map1的映射关系对数:"+map1.size());//测试---map1的映射关系对数:2
		System.out.println(map1);//{1=大侠, 2=小侠}
		//把key为1的value-"大侠"修改为“加油”
		map1.put(1, "加油");
		System.out.println("map1的映射关系对数:"+map1.size());//测试---map1的映射关系对数:2
		System.out.println(map1);//{1=加油, 2=小侠}
	}
}

演示代码3:

/*存储一个学生的信息,key是学号,value是一个集合,存储的是个人信息*/
public class MapTest03 {
	@Test
	public void Test04() {
		Map<Integer, ArrayList<String>> map1 = new HashMap<>();
		ArrayList<String> list = new ArrayList<>();
		list.add("姓名");
		list.add("学校");
		list.add("学院");
		list.add("专业");
		map1.put(20190725, list);
		System.out.println("map1的映射关系对数:"+map1);//map1的映射关系对数:{20190725=[姓名, 学校, 学院, 专业]}
		
	}
}

(2)void putAll(Map<?extends K,?extends V> m): 从指定映射中将所有映射关系复制到此映射中(可选操作)。

演示代码4:

/*存储一个小组的选手的信息,key是编号,value是姓名*/
public class MapTest04 {
	@Test
	public void Test2() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		Map<Integer,String> map2 = new HashMap<>();
		map2.put(3, "大白");
		map2.put(4, "小白");
		map1.putAll(map2);
		System.out.println("map1的映射关系对数:"+map1.size());//map1的映射关系对数:4
		System.out.println("map2的映射关系对数:"+map2.size());//map2的映射关系对数:2
	}
}

②删除

(3)void clear(): 从此映射中移除所有映射关系(可选操作)。

(4)V remove(Object key): 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。

演示代码5:

/*存储一个选手的信息,key是编号,value是姓名*/
public class MapTest05 {
	@Test
	public void Test3() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		String value=map1.remove(1);
		System.out.println("map1根据key删除的value:"+value);//map1根据key删除的value:大侠
		System.out.println("map1删除后映射关系对数:"+map1.size());//map1删除后映射关系对数:1
		
	}
}

③查询

(5)V get(Object key):返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null

(6)boolean containsKey(Object key): 如果此映射包含指定键的映射关系,则返回 true

(7)booleab caontainsValue(Object key): 如果此映射将一个或多个键映射到指定值,则返回 true

演示代码6:

	/*存储一个选手的信息,key是编号,value是姓名*/
	public class MapTest06 {
		@Test
		public void Test06() {
			Map<Integer, String> map1 = new HashMap<>();
			map1.put(1, "大侠");
			map1.put(2, "小侠");
			System.out.println(map1.containsKey(1));//true
			System.out.println(map1.containsValue("大侠"));//true
			
		}	
}

(8)boolean isEmpty(): 如果此映射未包含键-值映射关系,则返回 true

元视图操作(遍历)>

(9)Set KeySet() :这种方式遍历所有的key,是因为Set中所有的key不能重复,所以Set系列可以根据key获取value;

代码演示7:

/*Set<K> KeySet(),遍历所有的key*/
public class MapTest07 {
	@Test
	public void Test() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		Set<Integer> keySet = map1.keySet();
		System.out.println("遍历映射中的key值:"+keySet);//遍历映射中的key值:[1, 2]
		for (Integer key : keySet) {
			System.out.println("遍历映射中的key值:"+key);
			/*
			 遍历映射中的key值:1
			 遍历映射中的key值:2
			*/
		}
	}
}

(10)Collection values():这种方式遍历所有的Value,因为Set系列的value可能重复,所以不能用Set系列;

代码演示8:

/*Set<K> KeySet(),遍历所有的Value*/
public class MapTest08 {
	@Test
	public void Test() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		Collection<String> values = map1.values();
		System.out.println("遍历映射中的value值:"+values);//遍历映射中的value值:[大侠, 小侠]
		for (String value : values) {
			System.out.println("遍历映射中的value值:"+value);
			/*
			遍历映射中的value值:大侠
            遍历映射中的value值:小侠
			*/
		}
	}
}

(11)Set<Entry<K,V>> entrySet():这种方式遍历所有的映射关系,因为所有的key不可能重复,那么映射关系的组合也不会重复,因为添加到map中(key,value)最后会封装成一个Entry的结点对象放到map中。

说明:Entry是Map接口的内部接口

代码演示9:

/*Set<Entry<K,V>> entrySet() 遍历所有的映射关系*/
public class MapTest09 {
	@Test
	public void Test() {
		Map<Integer, String> map1 = new HashMap<>();
		map1.put(1, "大侠");
		map1.put(2, "小侠");
		Set<Entry<Integer, String>> entrySet = map1.entrySet();
		System.out.println("遍历所有映射关系:"+entrySet);//遍历所有映射关系:[1=大侠, 2=小侠]
		for (Entry<Integer, String> entry : entrySet) {
			System.out.println(entry);
			/*
			1=大侠
			2=小侠
			*/
		}
	}
}

⑤数量

(12)int size(): 返回此映射中的键-值映射关系数。

3.Map接口的实现类

HashMap<K,V>(哈希表)

Hashtable<K,V>(哈希表)

TreeMap<K,V>

LinkedHashMap:是HashMap的子类

Properties:是Hashtable的子类

4.HashMap与Hashtable的区别(面试常问)

①相同点:它们都是底层原理都是哈希表

②不同点:

(1)Hashtable:旧版的–JDK1.0,线程是安全的(同步的),不允许key和value为null值;

(2)HashMap:新版的–JDK1.2,线程不安全的(不同步的),在多线程场景下要手动同步,允许key和value为null值

(3)HashTable使用Enumeration进行遍历,HashMap使用Iterator进行遍历。

(4)HashMap中hash数组的默认大小是16,而且一定是2的指数;HashTable中hash数组默认大小是11,增加的方式是 old*2+1。

(5)哈希值的使用存在差异,HashTable直接使用对象的hashCode,而HashMap重新计算hash值,而且用“&”代替求模。

5.哈希表、TreeMap与LinkHashMap的区别(面试常问)

①哈希表:遍历顺序与添加的顺序无关,即不能保证添加的顺序。

②TreeMap:按照key的大小顺序进行遍历,即TreeMap要么实现java.lang.Comparable接口,要么实现在创建TreeMap是指定Comparator的的对象(实质就是内部类的应用)。

③LinkHashMap:遍历的顺序是可以保证添加的顺序。

6.Properties(注意)

Properties 类是 Hashtable 的子类,Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。在存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法。
Properties比较特殊,它的key和value必须是String的类型,这里需要注意。

public class MapTest {
	@Test
	public void Test() {
		Properties properties = System.getProperties();// 系统类System
		Set<Entry<Object, Object>> entrySet = properties.entrySet();// 遍历Map
		for (Entry<Object, Object> entry : entrySet) {
			System.out.println(entry);//打印本地系统属性
		}
	}
}

7.Set的底层都是Map来实现

☛思考:我们知道存入Set的是一个对象,但是Map是一对,那另一个怎么来呢?

例如1:	TreeSet<String> set = new TreeSet<>();
		  set.add("林大侠");
例如2:	Map<String,String> map =new Map<>();
		  map.put("林大侠""你好")

有这样的可以解释:添加到Set中的元素(元素不可重复的特点)作为了底层Map的key值,Map的value是一个Object类型的PRESENT常量对象

查看(JDK1.6)源码1:

  private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

▲可以看到,我们实例化HashSet的时候,返回的是HashMap的对象map。

查看(JDK1.6)源码2:

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

▲可以看到,在实现add()方法中,实际上调用的是map的put方法,我们发现添加的元素作为map的key,而value则是一个静态的Object类型的常量,而这个常量我们用不到。


问题一:为什么Set要使用到底层Map?

因为设计出一种新的数据结构比较难,又因为Set与Map的key有很大的相同点,都是不可重复。

问题二:为什么要用一个PERSENT常量对象?

主要目的就是为了减少value对象对内存资源的浪费,共享同一个静态的常量对象


☛推荐阅读往期博文:

JavaSE集合篇#List之实现类ArrayList&Vector&LinedList&Stack浅析

JavaSE集合篇#Set之实现类HashSet&TreeSet&LinkedHashSet浅析

建议收藏|JavaSE集合篇#Collection&Map等系列#结构关系图解

#轻松一刻

在这里插入图片描述


☝上述分享来源个人总结,如果分享对您有帮忙,希望您积极转载;如果您有不同的见解,希望您积极留言,让我们一起探讨,您的鼓励将是我前进道路上一份助力,非常感谢!我会不定时更新相关技术动态,同时我也会不断完善自己,提升技术,希望与君同成长同进步!

☞本人博客:https://coding0110lin.blog.csdn.net/  欢迎转载,一起技术交流吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值