java中简单集合框架(二)
来源:互联网 发布:mac怎么下b站视频 知乎 编辑:程序博客网 时间:2024/06/06 01:07
一.简单的用法
下面我们在来讨论讨论Map接口,其中Map接口中的实现类为HashMap.
常用的方法:
map.clear(); //清除整个链表
map.remove(); //删除指定键对应的Entry节点
map.put(T k,T v)(); //放数据
set ketSet(); //得到键的集合
set<Entry> entrySet(); //得到Entry集合
代码:
public class TestMap {
public static void main(String[] args) {
//生成新闻对象
Date d=new Date();
News cars=new News("凯迪拉克", "黄渤", d);
News sports=new News("乔丹", "黄渤", d);
News house=new News("SOHO", "潘石屹", d);
News animal=new News("duck", "唐老鸭", new Date());
//生成HashMap容器
HashMap map=new HashMap();
map.put("cars",cars);
map.put("sports", sports);
map.put("house", house);
map.put("animal", animal);
/**遍历map的第一种方式
遍历的方式获得map集合中Key的集合,通过Key获得Value
**/
Set set=map.keySet();
Iterator it=set.iterator();
//这样可以在一定程度上节约内存。在栈中只有一份key,value;
Object key=null;
Object value=null;
while(it.hasNext()){
key=it.next();
System.out.print(key);
value=map.get(key);
System.out.println(value);
}
/**遍历map的第二种方式
通过map.entrySet()获得键值对的集合
然后遍历集合,得到每个节点。
**/
Set entrys=map.entrySet();
Iterator it2=entrys.iterator();
Object obj;
while(it2.hasNext()){
obj=it2.next();
System.out.println(obj);
}
}
}
二.对hashMap的初步认识(拷贝的源代码):
HashMap 是我们最常使用的具有映射关系的容器,在HashMap中键值对是一一对应的,也就是一个键只能对应一个值。如果我们通过hashMap中的put(key,value)放入值得时候,如果在HashMap中有此键,此时,将
将覆盖此键对应的值。由于键是唯一的,所以在HashMap中只有唯一一个空键。
HashMap的底层实现为数组链表。我们在开辟容量大小的时候,其实开辟的是对象数组。此时的数组的类型为
Entry类型,下面我们在来看看Entry的类型定义。Entry的属性为key,value,next,hash.
1.下面我们来看看这几个字段:
key: 键,用final关键字修饰,也就是说这个键一旦确定就不能更改。
value: 没有用final修饰,所以可以改变,对应的场景就是,你如果通过put()方法往容器中添加键值对时,如果在此数组链表中有含有此键的Entry节点,此时不会生成新的结点,只是将原来
数组数组链表中结点的value值更改为新的value值。
next:表示指向下一个节点。这样节点与节点就会串联起来。生成数组链表。
hash:每个节点对象中都有一个hash值,这个值是用来刻画Entry节点中key(键)的。如果键值没有重写Hashcode()方法,则此键的hashcode()就为object的hashcode()即为对象的地址。
在此基础上再调用一下Hash()函数来计算的一个值。如果重写了hashcode()方法,就为重写的hashcode()求出的值在调用hash()函数来求的。这样做的目的是为了快速找到结点中相同的键,
如果hash不同,则说明键肯定不同。这样就会比较下一个节点的key值。可以提高查询效率。
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
//这个不懂。
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
//重写了Equls方法.
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
2.下面我们来简单描述一下put(key,value)方法的执行过程。
分为两种情况:
1.如果键值为null。则会执行putForNullKey方法,由于键值为空所以会在table[0]//这一行链表中
如果已经存在键值为null的,此时会覆盖value值,如果不存在,就会添加新的Entry节点。
2.如果键值不为null,则会根据形参传递的key,先求此key的Hash,然后通过此hash定位到此Entry结点在数组的哪一行上的单链表。
然后依次遍历整条单链表,如果存在就会覆盖,如果没有就会在table[i]的头插入,其他节点在后面。
1. public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
//确定在哪行单链表中
int i = indexFor(hash, table.length);
//如果存在就覆盖
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//不存在就会新创结点
addEntry(hash, key, value, i);
return null;
}
3.下面看看 函数indexFor()
static int indexFor(int h, int length) {
return h & (length-1);
}
在看这个函数之前必须看看构造函数,
这个构造函数保证开辟的数组容量一定为2的n次幂。也就是Entry[]table永远为2的n次幂。length-1表示的二进制数位11111..11,所有位都为1,那么拿h&(length-1),我们可以将节点均匀的
分布到table[]数组上,,例如:length = 8,则 1&7=1,2&7=2,3&7=3...7&7=7,8&7=0,9&7=1; 其效果和探测散列中hash对数组取余效果一样。
public HashMap(int initialCapacity, float loadFactor) {
// Find a power of 2 >= initialCapacity
int capacity = 1;
//找到一个最小的不小于指定容量的数据量,此数据量为2的n次幂。
//用的为左移
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
//从头开始遍历所有的数组中的节点,单这样效率不高。
4. public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
5. final Entry<K,V> getEntry(Object key) {
//求hash值
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
这个地方用了短路与,通过hash可以提高比较速度。
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
判定是否包含指定的键,此时调用了getEntry方法,通过键找到Entry节点,看看是否能找到。
6.public boolean containsKey(Object key) {
return getEntry(key) != null;
}
7.HashMap的扩容机制
void resize(int newCapacity) {
/**如果容量已经达到了最大此时就不再扩容**/
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
/**如果没有达到系统定义的最大容量,先开数组空间,
然乎在将原来数组中的节点映射到新的数组链表中
*/
Entry[] newTable = new Entry[newCapacity];
/*老数组中的节点映射到新的数组中*/
transfer(newTable);
/*返回值*/
table = newTable;
/**新的临界值,也就是当节点的个数大于此值时,就会重新扩容*/
threshold = (int)(newCapacity * loadFactor);
}
/**
* /*老数组中的节点映射到新的数组中*/
*/
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
- java中简单集合框架(二)
- Java集合框架 (二)
- JAVA集合框架(二)
- Java集合框架(二)
- Java集合框架二
- Java基础--集合框架<二>
- Java集合框架(二)
- Java集合框架(二)--Collection
- java集合框架(二)
- Java集合框架简单总结
- java 集合框架 简单介绍
- java集合框架简单比较
- Java集合框架(中)
- Java集合框架(中)
- Java中list集合框架
- Java中set集合框架
- Java中map集合框架
- Java集合框架全面介绍(二)
- 黑马程序员--常用API
- iOS CocoaPods使用
- Ubuntu 安装QEMU--30days diy os
- C#面向对象编程全面总结-面试|复习必备
- CABasicAnimation-核心动画
- java中简单集合框架(二)
- iOS 单例模式
- switch...case语句
- android使用CheckedTextView搭配listview完成选择列表
- jQuery下级菜单
- Android中WebView调用拨号盘
- HDU 4411 Arrest 最小费用最大流(题意+建图)
- iOS开发:iOS TableViewCell自定义分割线
- Linux命令TR