import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* HashMap:
* 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
* (除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变
*
* 注意
* 此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。
* (结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)
* 这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。
* 最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
* Map m = Collections.synchronizedMap(new HashMap(...));
* 由所有此类的“collection 视图方法”所返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,
* 其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败
* 而不冒在将来不确定的时间发生任意不确定行为的风险。
*
*
* 底层采用哈希表作为数据结构
* HashMap默认分组:16,必须是2的N次方数
* //The default initial capacity - MUST be a power of two.
* static final int DEFAULT_INITIAL_CAPACITY = 16
*
* HashMap扩容机制: 分组组数*2
* HashMap的主键和值都可以为null
* HashMap是多线程的
* hashCode&(分组组数-1)
* -------------------------------------
* HashMap的基本用法:
* 1.创建HashMap -> new HashMap<K,V>();
* 2.添加元素 -> put(K,V);
* 在HashSet当中,添加元素被认为是相同元素时,新元素舍弃,老元素不变
* 在HashMap当中,由于HashSet就是HashMap的主键部分,所以HashMap的主键相同,在主键被认定为相同元素的时候,主键不变 而值进行覆盖
* 3.获得元素个数 -> map.size();
* 4.判断是否包含主键 -> containsKey(K); boolean
* 5.判断是否包含值 -> containsValue(V); boolean
* 6.删除元素 -> remove(K)
* 7.遍历
* 1.根据主键: Set<String> set = hm.keySet(); key
* 2.根据值 : Collection<Integer> set = hm.value(); value
* 3.Set<Map.Entry<String, Integer>> set3 = hm.entrySet(); key=value
* 4.Iterator<String> car = hm.keySet().iterator();
*
* ==============================================================================================================
* Hashtable
* 此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
* 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
* Hashtable 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。
* 注意
* 哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。
* 初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
*
* 下面这个示例创建了一个数字的哈希表。它将数字的名称用作键:
*
* Hashtable<String, Integer> numbers
* = new Hashtable<String, Integer>();
* numbers.put("one", 1);
* numbers.put("two", 2);
* numbers.put("three", 3);
* 要获取一个数字,可以使用以下代码:
*
* Integer n = numbers.get("two");
* if (n != null) {
* System.out.println("two = " + n);
* }
* }
* 注意
* 迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。
* 快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。
* 因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测程序错误。
*
* Hashtable 线程是同步的 是synchronized修饰的
*-------------------------------------------------------------------------------------------------
*Hashtable 的基本用法跟HashMap基本一致,不同点在于 HashTable是同步的,而HashMap是非同步的
* Hashtable 底层采用哈希表作为数据结构
* Hashtable 默认分组16,分组组数是任意的
* Hashtable 扩容机制 分组组数*2+1
* Hashtable 主键和值都不允许为null
* Hashtable 线程是安全的
* hashCode%分组组数
*==================================================================================================
*ArrayList 和 Vector 的区别:
* ArrayList和Vector底层都是采用数组作为数据结构
* Vector 是同步的,ArrayList是非同步的
* ArrayList底层默认分配10块空间,扩容机制:*3/2+1 1.5倍扩容
* public void ensureCapacity(int minCapacity) {
* modCount++;
* int oldCapacity = elementData.length;
* if (minCapacity > oldCapacity) {
* Object oldData[] = elementData;
* int newCapacity = (oldCapacity * 3)/2 + 1;
* if (newCapacity < minCapacity)
* newCapacity = minCapacity;
* // minCapacity is usually close to size, so this is a win:
* elementData = Arrays.copyOf(elementData, newCapacity);
* }
* }
* Vector 底层虽然也是默认11块空间.但扩容机制是*2
* private void ensureCapacityHelper(int minCapacity) {
* int oldCapacity = elementData.length;
* if (minCapacity > oldCapacity) {
* Object[] oldData = elementData;
* int newCapacity = (capacityIncrement > 0) ?
* (oldCapacity + capacityIncrement) : (oldCapacity * 2);
* if (newCapacity < minCapacity) {
* newCapacity = minCapacity;
* }
* elementData = Arrays.copyOf(elementData, newCapacity);
* }
* }
*HashMap 和 Hashtable 的区别:
* HashMap 是多线程的(非同步),Hashtable 是单线程的(同步的)
* HashMap 底层默认16组,扩容机制是分组组数*2
* HashMap 允许主键和值都为空,当添加主键相同的元素时,逐主键不变,值进覆盖
* Hashtable 主键和值都不可以为空 "" 和 null 是不一样的 ""可以存放,null或导致Hashtable 抛出空指针异常
* Hashtable 默认11组,扩容机制是*2+1
*
* */
public class TestHashMap2{
public static void main(String[] args){
HashMap_Basic_Usage hbu = new HashMap_Basic_Usage();
//hbu.create();
TestHashtable ht = new TestHashtable();
ht.show();
}
}
//HashMap的基本用法
class HashMap_Basic_Usage{
public void create(){
//创建HashMap
HashMap<String,Integer> hm = new HashMap<String,Integer>();
//添加基本数据类型元素
hm.put("tom", 18);
hm.put("jreey", 19);
//测试添加空值
hm.put(null,null);//当两个主键相同的时候,主键不变 值进行覆盖 后来的覆盖原有的
hm.put(null, 100);//
hm.put("etoak", 500);
//获得hm元素映射数量
//System.out.println(hm.size());
//根据主键获得对应的值
//System.out.println(hm.get("tom"));
//isEmpty判断集合是否为空
//System.out.println(hm.isEmpty());
//containsKey 集合中是否包含主键 返回boolean 类型
//System.out.println(hm.containsKey("jreey"));
//containsValue 集合中是否包含值 - boolean
//System.out.println(hm.containsValue(21));
//根据主键 删除元素
//hm.remove("tom");
//遍历
//1. 使用Set接口 根据主键进行遍历
Set<String> set = hm.keySet();
for(String str:set){
//System.out.println(str);
//System.out.println("Key is:"+str+" Value is:"+hm.get(str));
}
//2. 使用Collection接口 根据值进行遍历
Collection<Integer> set2 = hm.values();
for(Integer in:set2){
//System.out.println(in);
}
//3.
Set<Map.Entry<String, Integer>> set3 = hm.entrySet();
for(Map.Entry me:set3){
//System.out.println(me);
//System.out.println("Key is:"+me.getKey()+" Value is:"+me.getValue());
}
//Iterator
Iterator<String> car = hm.keySet().iterator();
while(car.hasNext()){
String str = car.next();//返回的是Object类型 要么强转为String类型或是.toString()或是+"",再或是在创建迭代器的时候 直接指定迭代的要迭代的类型
System.out.println(str+"="+hm.get(str));
}
}
}
//Hashtable
class TestHashtable{
public void show(){
Hashtable<Integer,String> ht = new Hashtable<Integer,String>();
ht.put(1, "");
ht.put(2, "2");
ht.put(1, "1");
//ht.put(null, "3");
//ht.put(4, null);
Set<Map.Entry<Integer, String>> set = ht.entrySet();
for(Map.Entry me:set)
System.out.println(me);
}
}
HashMap 补充:
import java.util.*;
public class TestHashMap2{
public static void main(String[] args){
HashMap<Student,String> hm = new HashMap<Student,String>();
/**
在HashSet当中,当添加的元素被认为是相同的对象的时候
新元素舍弃老元素不变!
在HashMap当中,由于HashSet就是HashMap的主键部分
所以HashMap的主键同样,在添加主键被认为是相等的元素
的时候,主键不变,而值进行覆盖
*/
Student s1 = new Student("小白",20);
Student s2 = new Student("小黑",20);
hm.put(s1,s1.name);
hm.put(s2,s2.name);
System.out.println(hm);
}
}
class Student{
String name;
int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return name + " " + age;
}
public boolean equals(Object obj){
if(obj == null) return false;
if(!(obj instanceof Student)) return false;
if(obj == this) return true;
Student s1 = (Student)obj;
return s1.age == this.age;
}
public int hashCode(){
return age;
}
}