→阿童沐

財富==支撐一個人生存多長時間的能力!

导航

Map-

Map<K, V>表示映射关系,是将Value映射至Key上。已经实现该接口的类有HashMap<K, V>。

与HashSet相类似,HashMap也是与hashCode()方法的实现有关.

同HashSet相类似,HashMap放进去的元素的顺序仍然是无序的,如下例:

package cn.edu.bupt.array;

import java.util.HashMap;
import java.util.Map;

public class MapTest1
{
public static void main(String[] args)
{
Map<String, String> map = new HashMap<String, String>();

map.put("a", "zhangsan");
map.put("b", "lisi");
map.put("c","wangwu");

System.out.println(map);
}
}

结果:

注意:Map中不允许有重复的键(Key),如果假如重复的键和新值,将会产生覆盖,即使用新的值覆盖当前Map.Entry已存在的值,而不是像HashSet一样不允许加入。

例如:

package cn.edu.bupt.array;

import java.util.HashMap;
import java.util.Map;

public class OverrideValue
{
public static void main(String[] args)
{
Map<String, String> map = new HashMap<String, String>();

map.put("a", "zhangsan");
map.put("b", "lisi");
map.put("c","wangwu");

System.out.println("------before override---------");
System.out.println(map);

System.out.println("-------after override-----------");
map.put("a", "zhaoliu");
System.out.println(map);
}
}

结果如下:




Map中的键(Key)绝对不允许含有重复内容,但是值(Value)允许含有重复内容,因此Map.keySet()返回Set<K>类型的对象,Map.values()返回Collection()类型的对象,因为Set不允许含有重复元素,而Collection允许含有重复元素。

如下例:

package cn.edu.bupt.array;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class MapTest1
{
public static void main(String[] args)
{
Map<String, String> map = new HashMap<String, String>();

map.put("a", "zhangsan");
map.put("b", "zhangsan");
map.put("c", "wangwu");

Collection<String> col = map.values();
for(String s : col)
{
System.out.println(s);
}
}
}

打印结果:

 


遍历Map的第一种方式(使用keySet()和get()方法):

package cn.edu.bupt.array;

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

public class MapTest1
{
public static void main(String[] args)
{
Map<String, String> map = new HashMap<String, String>();

map.put("a", "zhangsan");
map.put("b", "lisi");
map.put("c", "wangwu");
map.put("a", "zhaoliu");

Set<String> set = map.keySet();
for (Iterator<String> keys = set.iterator(); keys.hasNext();)
{
String key = keys.next();
String value = map.get(key);
System.out.println("" + key + ":" + value);
}

}
}

 

例:统计相同的单词出现的次数

package cn.edu.bupt.array;

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


public class MapTest1
{
public static void main(String[] args)
{
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i=0; i<args.length; i++)
{
if(map.containsKey(args[i]))
{
map.put(args[i], map.get(args[i]) + 1);
}
else
{
map.put(args[i], 1);
}
}

Set<String> set = map.keySet();
for (Iterator<String> keys = set.iterator(); keys.hasNext();)
{
String key = keys.next();
int value = map.get(key);
System.out.println(key + "=" + value);
}
}
}

 


 遍历Map的第二种方式:(通过Map.Entry<K, V>接口进行迭代,这种方式比较方便)

package cn.edu.bupt.array;

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

public class EntrySetTest
{
public static void main(String[] args)
{
Map<String, String> map = new HashMap<String, String>();

map.put("a", "aa");
map.put("b", "bb");
map.put("c", "cc");

Set<Map.Entry<String, String>> set = map.entrySet();

for(Iterator<Map.Entry<String, String>> entrySet = set.iterator(); entrySet.hasNext();)
{
Map.Entry<String, String> entry = entrySet.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}

Map.Entry<K, V>反应的是一个Map中的一个个K-V对.
两种遍历Map对象方式比较:    四十九-12:00


 

使用Map方式对50个随机数进行统计排序输出(方法1):

package cn.edu.bupt.array;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

public class Homework
{
public static void main(String[] args)
{
Map<Integer, Integer> map = new HashMap<Integer, Integer>();

Random random = new Random();

for (int i = 0; i < 50; i++)
{
int randomNum = random.nextInt(41) + 10;
if (map.containsKey(randomNum))
{
map.put(randomNum, map.get(randomNum) + 1);
}
else
{
map.put(randomNum, 1);
}
}
Set<Integer> keySet = map.keySet();
List<Integer> list = new ArrayList<Integer>(keySet);
//排序,使用匿名内部类
Collections.sort(list, new Comparator<Integer>()
{
public int compare(Integer o1, Integer o2)
{
return o1 - o2;
}
});

for (int num : list)
{
System.out.println("" + num + ":" + map.get(num));
}
}
}

方法2(使用TreeMap):

package cn.edu.bupt.array;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;

public class Homework
{
public static void main(String[] args)
{
//使用TreeMap对Integer类型的元素进行自然排序,这时可以不显式指定comparator
Map<Integer, Integer> map = new TreeMap<Integer, Integer>();

Random random = new Random();

for (int i = 0; i < 50; i++)
{
int randomNum = random.nextInt(41) + 10;
if (map.containsKey(randomNum))
{
map.put(randomNum, map.get(randomNum) + 1);
}
else
{
map.put(randomNum, 1);
}
}

Set<Map.Entry<Integer, Integer>> entrys = map.entrySet();
for (Map.Entry<Integer, Integer> entry : entrys)
{
Integer key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " : " + value);
}
}
}

程序执行结果如下:


 

对于一个大的项目的每一个模块,不要当把所有的代码写完成了再进行测试,要没写一个子模块就进行测试,一般使用JUnite进行单元测试。  五十-16:00


HashSet和HashMap的底层实现机制:

1、HashSet底层是使用HashMap实现的,如下源代码:  五十二-05:00

当使用add方法将对象添加到HashSet当中的时候,实际上是将该对象作为底层所维护的Map对象的Key,而Value则是同一个Object对象(该对象是一个假的(Dummy)对象):

 


2、HashSet.remove()方法

调用HashMao的remove方法

 

3、HashSet.iterator()方法:

 

4、HashSet.size()方法:

返回的是Map的size方法

 


哈希表概念:    五十二-18:15

在以前所提到的各种结构(线性表,树)中,记录在结构中的相对位置是随机的,和记录的关键字之间不存在确定的关系,因此,在结构中查找记录是需要进行一系列的关键字比较。这一类查找方法建立在比较的基础之上。

如果要想在不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字和结构中一个唯一的存储位置相对应。因而在查找时,只要根据这个对应关系f找到给定值K的像f(K)。若结构中存在关键字和K相等的记录,则必顶在f(K)的存储位置上,由此,不需要进行比较便可直接取得所查记录。再次,我们这个对应关系f为哈希(Hash)函数,按照这个思想建立的表为哈希表。

哈希表是通过哈希函数实现集合元素的快速查找定位方式.

HashMap底层仍然是维护一个数组,我们向HashMap中

在HashMap类中通过如下方法实现Hash表中的对应关系f:

HashMap中通过key的hashCode()进行散列,(因此这里可以理解为f(K)中的K就是这里的key.hashCode(),因为Java中的key可以是任意的对象,因此需要使用对象的hashCode()函数得到确定的整数值K),计算得到当前Entry<K, V>对在Entry[] table数组中的存储地址的参考值,然后再通过下一行indexFor(hash, table.length)计算得到的i才是真正的在table中的存储位置,如下图:

Entry<K, V>内部类如下:

在for循环中遍历table数组的每一个Entry,而if判断中的条件的主要内容就是在讲HashSet时,有关hashCode()和equals()的两个原则,详见HashSet.

这个for循环判断的是如果出现相同的key的情况,则相应的Value值进行覆盖,并且return原来Value值。

如果不存在相同的Key,则通过下面的函数将Entry加入:

如果size >= threshold则对数组进行扩容,使其变成原来的2倍。


HashMap底层实现结构如下:

原始Map结构      五十二-36:00 

加入Entry对象后的状态

当向HashMap中put一对键值对的时候,它会根据key的hashCode()值计算出来一个位置,该位置就是此对象准备向数组中放置的位置,但是具体是否要放入当前结构需要根据equals方法对当前链上的各个Entry元素进行比较。

1、如果该位置没有Entry对象存在,就将此对象直接链接进入数组中;

2、如果该位置已经有对象存在了,则顺着此存在对象链开始寻找(Entry类有一个指向Entry对象的next引用变量,指向了该Entry对象的下一个Entry对象)。

  如果此链上有对象的话,再去使用key.equals()方法进行比较,如果对此链上的所有对象的equals方法比较为false,则将该对象放到数组中,然后将数组中该位置以前存在的那些链表对象链接到此对象的后面(符合刚访问过的元素在不久的将来还会被访问到);

  如果存在一个Entry对象与要加入的对象equals方法比较为true,则使用新的Value去改写旧的Value,并返回旧的Value值。

总结:hashCode()方法遍历行,equals()方法遍历列。

所有集合的底层实现仅仅存在两种:数组链表,不会存在其他情况。    五十二-46:00

posted on 2012-03-21 14:39  阿童沐  阅读(263)  评论(0)    收藏  举报