Java基础之133集合框架Map接口 134HashMap源码分析与哈希表实现原理135 136TreeMap

集合框架Map接口
1、Map接口
public interface Map<K,V>
将键映射到值的对象,一个映射不能包含重复的键;每个键最多只能映射到一个值。

在这里插入图片描述


1、Map接口
在这里插入图片描述


2、HashMap
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。


3、Hashtable
public class Hashtable<K,V> extends Dictionary<K,V>
implements Map<K,V>, Cloneable, Serializable
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。


4、LinkedHashMap
public class LinkedHashMap<K,V>
extends HashMap<K,V> implements Map<K,V>
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。


5、TreeMap
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, Serializable
基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。


  • Map接口:

  • 1、键值对存储一组对象

  • 2、Key不能重复(唯一),Value可以重复

  • 3、具体的实现类:HashMap TreeMap Hashtable LinkedHashMap

  • 4、HashMap 与 Hashtable的区别?

  • 5、如何选择使用哪个?
    1)如果存一个 使用Collection 存一对 两个 用Map
    2)Map,其中单线程,不保证顺序,不排序 使用HashMap
    要排序 使用TreeMap
    要保证输入顺序 使用LinkedHashMap
    Hashtable是1.0就有的 比较古老 较少使用

  • 6、数据结构:数组、链表、二叉树(红黑树)、哈希表(数组+链表)、栈、队列


  • 基于二叉树的红黑树实现

  • LinkedHashMap是HashMap的子类,由于HashMap不能保正顺序恒久不变,此类使用一个双重链表来维护
  • 元素添加的顺序。

  • JDK1.0开始
  • 基于哈希表实现(数组+链表)
  • 默认数组大小为11,加载因子0.75
  • 扩充方式:原数组大小<<1 (*2) +1
  • 线程安全的,用在多线程访问时

  • HashMap的现实原理:
    • 1、基于哈希表(数组+链表+二叉树(红黑树) )1.8JDK
    • 2、默认加载因子为0.75,默认数组大小是16,
    • 3、把对象存储到哈希表中,如何存储?
    • 把key对象通过hash()方法计算hash值,然后用这个hash值对数组长度取余数(默认16),来决定该对KEY对象
    • 在数组中存储的位置 ,当这个位置 有多个对象时,以链表结构存储,JDK1.8后,当链表长度大于8时,链表将转换为
    • 红黑树结构存储。
    • 这样的目的,是为了取值更快,存储的数据量越大,性能的表现越明显
    • 4、扩充原理:当数组的容量超过了75%,那么表示该数组需要扩充,如何扩充?
    • 扩充的算法是:当前数组容量<<1 (相当于是乘2),扩大1倍, 扩充次数过多,会影响性能,每次扩充表示哈希表重新
    • 散列(重新计算每个对象的存储位置),我们在开发中尽量要减少扩充次数带来的性能问题。
    • 5、线程不安全,适合在单线程中使用

package com.vince;

import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

/**
 * Map接口:
 * 1、键值对存储一组对象
 * 2、Key不能重复(唯一),Value可以重复
 * 3、具体的实现类:HashMap TreeMap Hashtable LinkedHashMap
 * 4、HashMap 与 Hashtable的区别?
 * 
 * 5、如何选择使用哪个?
 * 
 * 6、数据结构:数组、链表、二叉树(红黑树)、哈希表(数组+链表)、栈、队列
 * @author vince
 * @description
 */
public class MapDemo {
	
	/**
	 * 基于二叉树的红黑树实现
	 */
	private static void treeMap(){
		Map<String,String> map = new TreeMap<>();
//		map.put("one", "Lily");
//		map.put("two", "Tom");
//		map.put("three", "Bin");
//		map.forEach((key,value)->System.out.println(key+"->"+value));
//		
		Map<Dog,String> dogs = new TreeMap<>();
		dogs.put(new Dog(1,"2ha",3), "dog1");
		dogs.put(new Dog(1,"wangwang",2), "dog2");
		dogs.put(new Dog(3,"hsq",4), "dog3");
		dogs.forEach((key,value)->System.out.println(key+"->"+value));
	}
	
	/**
	 * LinkedHashMap是HashMap的子类,由于HashMap不能保正顺序恒久不变,此类使用一个双重链表来维护
	 * 元素添加的顺序。
	 */
	private static void linkedHashMap(){
		Map<String,String> table = new LinkedHashMap<>();
		table.put("one", "Lily");
		table.put("two", "Tom");
		table.put("three", "Bin");
		table.forEach((key,value)->System.out.println(key+"->"+value));
	}
	
	/**
	 * JDK1.0开始
	 * 基于哈希表实现(数组+链表)
	 * 默认数组大小为11,加载因子0.75
	 * 扩充方式:原数组大小<<1 (*2) +1 
	 * 线程安全的,用在多线程访问时
	 */
	private static void hashtable(){
		
		Map<String,String> table = new Hashtable<>();
		table.put("one", "Lily");
		table.put("two", "Tom");
		table.put("three", "Bin");
		
		table.forEach((key,value)->System.out.println(key+"->"+value));
	}
	/**
	 * HashMap的现实原理:
	 * 1、基于哈希表(数组+链表+二叉树(红黑树) )1.8JDK
	 * 2、默认加载因子为0.75,默认数组大小是16,
	 * 3、把对象存储到哈希表中,如何存储?
	 * 	把key对象通过hash()方法计算hash值,然后用这个hash值对数组长度取余数(默认16),来决定该对KEY对象
	 * 在数组中存储的位置 ,当这个位置 有多个对象时,以链表结构存储,JDK1.8后,当链表长度大于8时,链表将转换为
	 * 红黑树结构存储。
	 * 这样的目的,是为了取值更快,存储的数据量越大,性能的表现越明显
	 * 
	 * 4、扩充原理:当数组的容量超过了75%,那么表示该数组需要扩充,如何扩充?
	 * 扩充的算法是:当前数组容量<<1 (相当于是乘2),扩大1倍, 扩充次数过多,会影响性能,每次扩充表示哈希表重新
	 * 散列(重新计算每个对象的存储位置),我们在开发中尽量要减少扩充次数带来的性能问题。
	 * 5、线程不安全,适合在单线程中使用
	 */
	private static void hashMap(){
		Map<Integer,String> map = new HashMap<>();
		map.put(1, "Tom");
		map.put(2, "Jack");
		map.put(3, "Vince");
		map.put(4, "Bin");
		map.put(5, "Lily");
		
		System.out.println("size="+map.size());
		//从MAP中取值
		System.out.println(map.get(1));//通过key取value
		
		//map的遍历 1 遍历Entry
		Set<Entry<Integer,String>> entrySet = map.entrySet();
		for(Entry e: entrySet){
			System.out.println(e.getKey()+"->"+e.getValue());
		}
		System.out.println("--------");
		//2 遍历键
		Set<Integer> keys = map.keySet();
		for(Integer i: keys){
			String value = map.get(i);
			System.out.println(i+"->"+value);
		}
		System.out.println("--------");
		//3 遍历值 
		Collection<String> values = map.values();
		for(String value: values){
			System.out.println(value);
		}
		System.out.println("--------");
		//4 foreach
		map.forEach((key,value)->System.out.println(key+"->"+value));
		
		System.out.println(map.containsKey(7));
		
		//hash
		Integer key = 1434;

		 System.out.println( 1434 % 16 );
		
	}

	public static void main(String[] args) {
//		hashMap();
//		hashtable();
		treeMap();
	}

}

Dog

package com.vince;

public class Dog implements Comparable<Dog>{

	private int id;
	private String name;
	private int age;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	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;
	}
	public Dog(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	public Dog() {
		super();
		// TODO Auto-generated constructor stub
	}
	@Override
	public String toString() {
		return "Dog [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
	@Override
	public int compareTo(Dog o) {
		return this.id-o.id;
	}
	
}

在这里插入图片描述

小结 当遇到Comparable报错时 该怎么解决呢
在这里插入图片描述
在这里插入图片描述
两种办法
一就是 在构造方法里面 给一个Comparator接口 具体使用方法:自行搜索 或者前边的博客应该有
在这里插入图片描述
二就是 在Dog实体类实现Comparable接口 记得重写CompareTo方法不然报错
在这里插入图片描述
在这里插入图片描述
在这里我们拿编号id比
在这里插入图片描述
此时就不会报错了
在这里插入图片描述

如果两个编号id是一样的(跟TreeSet是一样)
在这里插入图片描述在这里插入图片描述
注意这个地方有个毛病
看前两个加入的编号为id=1的 再看结果
如果要加入的key已经存在了 那么会使用原来的key 但是 value值会更新
这个地方要注意

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值