Java学习笔记——Map集合

在开发里面:Collection集合保存数据的目的是为了输出,Map集合保存数据的目的是为了进行key的查找。

1、Map接口简介

Map接口是进行二元偶对象保存的最大父接口,该接口定义如下:public interface Map<K, V>
在进行接口对象实例化的时候需要设计Key与Value的类型。3个重点操作方法:

  • 向集合之中保存数据:public V put(K key, V value)
  • 根据key查询数据:public V get(Object key)
  • 将Map集合转为Set集合:public Set<Map.Entry<K,V> entrySet()
  • 查询指定的key是否存在:public boolean containsKey(Object key)
  • 将Map集合中的key转为Set集合:public Set<K> keySet()
  • 根据key删除掉指定数据:public V remove(Object key)

【范例】Map使用

package demo;
import java.util.Map;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = Map.of("a",1,"b",2,"c",12);
		System.out.println(map);
		
	}
}
{a=1, b=2, c=12}

在Map集合之中数据的保存就是按照“key = value”的形式存储的,并且使用of()方法里面的数据key值是不能重复的。常用的子类:HashMap、Hashtable、TreeMap、LinkedHashMap。

HashMap子类

该类的主要特点是无序存储。定义:public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable,Serializable;
【范例】Map集合的使用

package demo;
import java.util.HashMap;
import java.util.Map;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("one",1);
		map.put("two",2);
		map.put("one",112);//key重复
		map.put(null,1);
		map.put("zeor",null);
		System.out.println(map.get("zero"));
		System.out.println(map.get("one"));//key存在
		System.out.println(map.get("null"));//key不存在
		System.out.println(map.get(null));//key存在
		System.out.println(map.get("ten"));//key不存在
		
	}
}
null
112
null
1
null

通过HashMap实例化的Map接口里面可以通过key或value保存null的数据,即便保存数据的key重复,也不会出现错误,而是出现内容的替换。
但是对于Map接口中提供的put()方法本身是提供有返回值的,那么这个返回值指的是在重复key的情况下返回旧的value。
【范例】put()方法使用

package demo;
import java.util.HashMap;
import java.util.Map;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new HashMap<String, Integer>();
		System.out.println(map.put("one", 1));//key不重复,返回value
		System.out.println(map.put("one", 121));//key重复,返回旧数据
		System.out.println(map);	
	}
}

在设置了相同key的内容的时候put()方法会返回原始的数据内容。

在进行HashMap的put()操作的时候,如何实现容量扩充的?

  • 在HashMap类里面提供有一个“DEFAULT_INITIAL_CAPACITY”常量,作为初始化的容量配置,而后这个常量的默认大小为16个元素,也就是默认的保存大小为16;
  • 当保存的内容容量超过了某个阈值(DEFAULT_LOAD_FACTOR = 0.75f),相当于“容量*阈值 = 12”保存12个元素的时候就会进行容量的扩充;
  • 在进行扩充的时候HashMap采用的是成倍的扩充模式,即:每一次都扩充2倍的容量。

请解释HashMap的工作原理:

  • 在HashMap中进行数据存储的依然是利用了Node类完成的,那么这种情况下证明可以使用的数据结构只有两种:链表(时间复杂度O(n))、二叉树(时间复杂度O(logn));
  • 从JDK1.8开始,HashMap发生了改变,因为其要适用于大数据时代的海量数据问题,所以对其存储发生了变换,并且在HashMap类的内部提供了一个重要的常量:static final int TREEIFY_THRESHOLD = 8;在使用HashMap的时候,如果没有超过阈值8(TREEIFY_THRESHOLD ),那么会按照链表的形式进行存储,而如果超过了这个阈值,则会将链表转为红黑树以实现树的平衡,并且利用左旋与右旋保存数据的查询性能。

LinkedHashMap

HashMap虽然是Map集合最为常用的一个子类,但是其本身所保存的数据都是无序的(有序与否对Map没有影响),如果现在希望Map集合之中保存的数据为其增加顺序,就可以更换子类为LinkedHashMap(基于链表实现的)。.

package demo;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new LinkedHashMap<String, Integer>();
		map.put("w", 12);
		map.put("s", 2);
		map.put("d", 32);
		map.put("e", 42);
		map.put("c", 52);
		System.out.println(map);
	}
}
{w=12, s=2, d=32, e=42, c=52}

Hashtable子类

与Vector、Enumeration属于最早的一批动态数组的实现类。定义如下:public class Hashtable<K,V> extends DIctionary<K,V> implements Map<K,V>,Cloneable, Serializable

package demo;
import java.util.Hashtable;
import java.util.Map;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String, Integer> map = new Hashtable<String, Integer>();
		map.put("w", 12);
		map.put("s", 2);
		map.put("d", 32);
		map.put("e", 42);
		map.put("c", 52);
		System.out.println(map);
	}
}
{w=12, s=2, e=42, d=32, c=52}

Hashtable里面进行存储的时候设置的key或value都不允许为null,否则报错NullPointerException。

HashMap与Hashtable的区别?

  • HashMap中的方法都属于异步操作(非线程安全),HashMap允许保存有null数据;
  • Hashtable中的方法都属于同步方法(线程安全),Hashtable不允许保存null,否则会出现NullPointerException。

Map.Entry接口

static class Node<K,V> implements Map.Entry<K,V>{}
所有的key和value的数据都被封装在Map.Entry接口之中,而此接口定义如下:
public static interface Map.Entry<K,V>
并且在这个内部接口里面提供有两个重要的操作方法:

  • 获取key:public K getKey();
  • 获取value:public V getValue()
package demo;
import java.util.Map;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map.Entry<String , Integer> entry = Map.entry("one", 2);
		System.out.println("获取Key:" + entry.getKey());
		System.out.println("获取value:" + entry.getValue());
		System.out.println(entry.getClass().getName());
	}
}
获取Key:one
获取value:2
java.util.KeyValueHolder

整个Map集合里面,Map.Entry的主要作用就说作为一个key和一个value的包装类型使用,大部分情况下进行数据存储的时候都会将key和value包装为一个Map.Entry对象进行使用。

利用Iterator输出Map集合

使用Iterator实现Map集合的输出须按照如下不走处理:

  • 利用Map接口中提供的entrySet()方法将Map集合转为Set集合;
  • 利用Set接口中的iterator()方法将Set集合转为Iterator接口实例;
  • 利用Iterator进行迭代输出获取每一组的Map.Entry对象,随后通过getKey()与getValue()获取数据
    【范例】利用Iterator输出Map集合
package demo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String , Integer> map= new HashMap<String, Integer>();
		map.put("one", 1);
		map.put("two", 2);
		Set<Map.Entry<String,Integer>> set = map.entrySet();
		Iterator<Map.Entry<String,Integer>> iter = set.iterator();
		while(iter.hasNext()) {
			Map.Entry<String, Integer> me = iter.next();
			System.out.println(me.getKey()+ "=" + me.getValue());
		}
	}
}

虽然Map集合本身支持有迭代输出的支持,但是如果从实际的开发来讲,Map集合最主要的用法在于实现数据key的查找操作,如果现在不使用Iterator而使用foreach语法输出也需要将Map转为Set集合
【范例】使用foreach输出

package demo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<String , Integer> map= new HashMap<String, Integer>();
		map.put("one", 1);
		map.put("two", 2);
		Set<Map.Entry<String,Integer>> set = map.entrySet();//将Map集合变为Set集合
		for(Map.Entry<String, Integer> entry:set) {
			System.out.println(entry.getKey() + "=" + entry.getValue());
		}
	}
}

关于KEY的定义

可以使用自定义的类来进行Key类型的设置。

package demo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Map<Person, String> map= new HashMap<Person,String>();
		Person p3 = new Person("张三", 12);
		map.put(p3,"duck");
		map.put(new Person("李四", 22),"dog");
		System.out.println(map.get(p3));
		System.out.println(map.get(new Person("李四", 22)));
	}
}
class Person{
	private String name; 
	private int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "name = " + this.name +"|age = " + this.age;
	}
}

虽然允许使用自定义的类作为key的类型,但是在实际开发之中常用的类型就三种:String、Long、Integer。尽量使用系统类。

如果在进行HashMap进行数据操作的时候出现了Hash冲突(Hash码相同),HashMap是如何解决的?
当出现了Hash冲突之后,为了保证程序的正常执行,会在冲突的位置上将所有Hash冲突的内容转为链表保存。

参考地址:https://edu.aliyun.com/lesson_1012_9091#_9091

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值