Java集合框架之HashMap

HashMap 的底层由一个散列表来实现,存储的内容是键值对(key-value),且键值不能重复,最多允许有一个null值。

1.Map与Set的关系

Set集合的特点是不能存储重复元素,不能保持元素插入时的顺序,且key值最多允许有一个null值。

由于Map中的key与Set集合特点相同,所以如果将Map中的value值当作key的附属的话,所有的key值就可以组成一个Set集合。

两者的实现类图也比较相似,见Java集合框架之基础

2.Map接口中定义的方法

Map接口中定义的部分重要方法如下:

public interface Map<K,V> {

boolean containsKey(Object key);

boolean containsValue(Object value);

V get(Object key);

V put(K key, V value);

V remove(Object key);

Set<K> keySet();                 // 由于Map集合的key不能重复,key之间无顺序,所以Map集合中的所有key就可以组成一个Set集合

Collection<V> values();          // 获取所有的values集合

Set<Map.Entry<K, V>> entrySet(); // 获取所有的key-value键值对,由Map.Entry<K,V>来表示

interface Entry<K,V> {   // 表示key-value对实体

K getKey();

V getValue();

V setValue(V value);

boolean equals(Object o);

int hashCode();

}

}

对部分方法说明一下:

  1. values() 方法 通过调用这个方法就可以返回Map集合中所有的value值;
  2. keySet()方法 调用后可以得到所有Map中的 key值,这些值可以组成一个Set集合
  3. entrySet()方法   得到所有的Map中key-value键值对,以Set集合的形式存储。将key看作Set中的元素,value当作 key的附属即可。
  4. 为了能够更好的表示这个key-value值,接口中还定义了一个Entry<K,V>接口,并且在这个接口中定义了一些操作key和value的方法。

3.HashMap中定义的变量及构造函数

HashMap的变量如下:

public class HashMap<K,V>  extends AbstractMap<K,V>   implements Map<K,V>, Cloneable, Serializable{

static final int DEFAULT_INITIAL_CAPACITY = 16; // 默认的初始化容量大小,必须是2的平方

static final int MAXIMUM_CAPACITY = 1 << 30;    // 最大的容量

static final float DEFAULT_LOAD_FACTOR = 0.75f; // 指定负载因子

transient Entry[] table;   // 存储key-value对

transient int size;        // 存储的实际key-value对数量

int threshold;             // 所能容纳的key-value对极限

final float loadFactor;    // 负载因子

transient int modCount;    // 记录修改内容的次数

}

代码中有几个变量需要说明一下:

  1. table数组用来存储key-value对,在调整数组的大小时值肯定是2的平方。
  2. loadFactor 负载因子,增大值时可以减少Hash表(也就是Entry数组)所占用的内存空间,但会增加查询数据时时间的开销,而查询是最频繁的操作;减小值时会提高数据查询的性能,但是会增大Hash表所占用的内存空间,所以一般是默认的0.75。
  3. threshold 表示HashMap所能容纳的key-value对极限,如果存储的size数大于了threshold,则需要扩容了,值一般为capacity*loadFactor。

HashMap提供了几个构造函数,如下

public HashMap(int initialCapacity, float loadFactor) {

if (initialCapacity < 0)

throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);

if (initialCapacity > MAXIMUM_CAPACITY)

initialCapacity = MAXIMUM_CAPACITY;

if (loadFactor <= 0 || Float.isNaN(loadFactor))

throw new IllegalArgumentException("Illegal load factor: " + loadFactor);

// Find a power of 2 >= initialCapacity

int capacity = 1;

while (capacity < initialCapacity)        // 计算出大于initialCapacity的最小的2的n次方值

capacity <<= 1;

this.loadFactor = loadFactor;

threshold = (int)(capacity * loadFactor); // 设置容量极限

table = new Entry[capacity];              // 将刚计算出的capacity当作Entry数组的大小

init();

}

public HashMap(int initialCapacity) {

this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

public HashMap() {

this.loadFactor = DEFAULT_LOAD_FACTOR;

threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);

table = new Entry[DEFAULT_INITIAL_CAPACITY];

init();

}

public HashMap(Map<? extends K, ? extends V> m) {

this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);

putAllForCreate(m);

}

由第一个构造函数可以看出,其实际的capacity一般是大于我们指定的initialCapacity,除非initialCapacity正好是2的n次方值。

4.key-value存储实体 - Entry

首先看一下代表key-value对的Entry实现,如下:

private static class Entry<K,V> implements Map.Entry<K,V> { // 实现Map接口中定义的Entry接口

int hash;

K key;

V value;

Entry<K,V> next;

protected Entry(int hash, K key, V value, Entry<K,V> next) {

this.hash = hash;

this.key = key;

this.value = value;

this.next = next;

}

protected Object clone() {

return new Entry<>(hash, key, value,(next==null ? null : (Entry<K,V>) next.clone()));

}

public K getKey() {   // 获取key值

return key;

}

public V getValue() { // 获取value值

return value;

}

public V setValue(V value) { // 为value设置新的值

if (value == null)

throw new NullPointerException();

V oldValue = this.value;

this.value = value;

return oldValue;

}

public boolean equals(Object o) { // 通过比较key和value值来确定两个Entry是否相等

if (!(o instanceof Map.Entry))

return false;

Map.Entry e = (Map.Entry)o;

return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&

(value==null ? e.getValue()==null : value.equals(e.getValue()));

}

public int hashCode() {

return hash ^ (value==null ? 0 : value.hashCode());

}

public String toString() {

return key.toString()+"="+value.toString();

}

}

HashMap顾名思义就是使用哈希表来存储的,哈希表为解决冲突,采用了开放地址法和链地址法来解决问题。Java中HashMap采用了链地址法。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被hash后,得到数组下标,把数据放在对应下标元素的链表上。

当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例:

HashMap<String , Double> map = new HashMap<String , Double>();

map.put("语文" , 80.0);

map.put("数学" , 89.0);

map.put("英语" , 78.2);

HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置。

当程序执行 map.put("语文" , 80.0); 时,系统将调用"语文"的 hashCode() 方法得到其 hashCode 值——每个 Java 对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值。得到这个对象的 hashCode 值之后,系统会根据该 hashCode 值来决定该元素的存储位置。

5.添加元素

HashMap 类的 put(K key , V value) 方法的源代码

/*

*  当向HashMap中添加mapping时,由key的hashCode值决定Entry对象的存储位置,当两个 key的hashCode相同时,

*  通过equals()方法比较,返回false产生Entry链,true时采用覆盖行为

*/

public V put(K key, V value) {

if (key == null)

return putForNullKey(value);        //key为null的判断

int hash = hash(key.hashCode());        // 计算hash值

int i = indexFor(hash, table.length);  //计算保存在table数组索引的位置

for (Entry<K,V> e = table[i]; e != null; e = e.next) {

Object k;

/*

*  如果hash值相同且key值的索引或内容相同,则采取覆盖行为,然后返回旧值

*  这样不会对modCount进行加1操作,也就是说这样不认为是结构发生了改变

*/

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;

}

/*

*  当向HashMap中添加mapping时,由key的hashCode值决定Entry对象的存储位置,当两个 key的hashCode相同时,

*  通过equals()方法比较,返回false产生Entry链,true时采用覆盖行为

*/

public V put(K key, V value) {

if (key == null)

return putForNullKey(value);        //key为null的判断

int hash = hash(key.hashCode());        // 计算hash值

int i = indexFor(hash, table.length);  //计算保存在table数组索引的位置

for (Entry<K,V> e = table[i]; e != null; e = e.next) {

Object k;

/*

*  如果hash值相同且key值的索引或内容相同,则采取覆盖行为,然后返回旧值

*  这样不会对modCount进行加1操作,也就是说这样不认为是结构发生了改变

*/

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;

}

当key值为空时,调用putForNullKey()方法进行值的添加,如下:

private V putForNullKey(V value) {

for (Entry<K,V> e = table[0]; e != null; e = e.next) {

if (e.key == null) {

V oldValue = e.value;

e.value = value;

e.recordAccess(this);

return oldValue;

}

}

modCount++;

addEntry(0, null, value, 0);

return null;

}

当系统决定存储 HashMap 中的 key-value 对时,完全没有考虑 Entry 中的 value,仅仅只是根据 key 来计算并决定每个 Entry 的存储位置。这也说明了前面的结论:我们完全可以把 Map 集合中的 value 当成 key 的附属,当系统决定了 key 的存储位置之后,value 随之保存在那里即可。

如果key为null,则hashCode值为0。所以需要遍历table[0]处的Entry链。如果已经存在null键,则覆盖并返回原始值。否则调用addEntry()方法进行添加。

根据 hashCode() 返回值来计算 Hash 值的方法,并且会调用 indexFor() 方法来计算该对象应该保存在 table 数组的哪个索引,源代码如下:

static int hash(int h) {

h ^= (h >>> 20) ^ (h >>> 12);

return h ^ (h >>> 7) ^ (h >>> 4);

}

static int indexFor(int h, int length) {

return h & (length-1);

}

hash(int h)方法根据key的hashCode重新计算一次散列。此算法加入了高位计算,防止低位不变,高位变化时,造成的hash冲突

我们可以看到在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的 元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。

  对于任意给定的对象,只要它的 hashCode() 返回值相同,那么程序调用 hash(int h) 方法所计算得到的 hash 码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大的,在HashMap中是这样做的:调用 indexFor(int h, int length) 方法来计算该对象应该保存在 table 数组的哪个索引处。

这个方法非常巧妙,它通过 h & (table.length -1) 来得到该对象的保存位,而HashMap底层数组的长度总是 2 的n 次方,这是HashMap在速度上的优化。

当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。举例如下:假设数组长度分别为16,优化后的hash码分别为8和9,那么&运算后的结果如下:

-----------------------------------------------------------------------------------------------------------------------------------------------------

8 & (16-1):                                 0100                   &              1111                   =                0100(8)

9 & (16-1):                                 0101                   &              1111                   =                0101(9)

----------------------------------------------------------------------------------------------------------------------------------------------------

当调用put()方法向 HashMap 中添加 key-value 对,由其 key 的 hashCode() 返回值决定该 key-value 对(就是Entry 对象)的存储位置。当两个 Entry 对象的 key 的 hashCode() 返回值相同时,将由 key 通过 eqauls() 比较值决定是采用覆盖行为(返回 true),还是产生 Entry 链(返回 false),而且新添加的 Entry 位于 Entry 链的头部,这是通过调用addEntry()方法来完成的,源码如下:

void addEntry(int hash, K key, V value, int bucketIndex) {

if ((size >= threshold) && (null != table[bucketIndex])) {  // 如果 Map 中的 key-value 对的数量超过了极限

resize(2 * table.length);                     //  把 table 对象的长度扩充到 2 倍

hash = (null != key) ? hash(key) : 0;

bucketIndex = indexFor(hash, table.length);

}

createEntry(hash, key, value, bucketIndex);

}

void createEntry(int hash, K key, V value, int bucketIndex) {

Entry<K,V> e = table[bucketIndex];                 // 获取指定 bucketIndex 索引处的 Entry

table[bucketIndex] = new Entry<>(hash, key, value, e);  // 将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry

size++;

}

将新添加的 Entry 对象放入 table数组的 bucketIndex 索引处。如果 bucketIndex 索引处已经有了一个 Entry 对象,那新添加的 Entry 对象指向原有的 Entry 对象(产生一个 Entry 链),如果 bucketIndex 索引处没有 Entry 对象, e 变量是 null,也就是新放入的 Entry 对象指向 null,也就是没有产生 Entry 链。

当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize

resize时,HashMap使用新数组代替旧数组,对原有的元素根据hash值重新就算索引位置,重新安放所有对象;resize是耗时的操作。resize的具体实现原理说明参考http://my.oschina.net/placeholder/blog/180069

那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12(这个值就是代码中的threshold值,也叫做临界值)的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

6.获取元素

get()方法的源代码如下:

public V get(Object key) {

if (key == null)

return getForNullKey();

int hash = hash(key.hashCode());

// 搜索该Entry链的下一个Entry,有多个Entry链时必须顺序遍历,降低了索引的速度

// 如果Entry链过长,说明发生“Hash”冲突比较频繁,需要采用新的算法或增大空间

for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {

Object k;

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

return e.value;

}

  return null;

}

当 HashMap 的每个 bucket 里存储的 Entry 只是单个 Entry 时的 HashMap 具有最好的性能:当程序通过 key 取出对应 value 时,只要先计算出该 key 的 hashCode() 返回值,在根据该 hashCode 返回值找出该 key 在 table 数组中 的索引,然后循环遍历查找 hash值相同,key值相同的value。

key为空值时调用getForNullKey()方法,源代码如下:

private V getForNullKey() {

for (Entry<K,V> e = table[0]; e != null; e = e.next) {

if (e.key == null)

return e.value;

}

return null;

}

key为null的Entry只可能存在于索引值为0处,遍历这个链即可。

7.获取Entry集合

先例举一个常用的例子

HashMap map=new HashMap();

map.put("a", "value1");

map.put("b", "value2");

map.put("c", "value3");

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

Iterator iter=s.iterator();

while(iter.hasNext()){

Map.Entry entry = (Map.Entry) iterator.next();

String keyName = (String)entry.getKey();

String valueName = (String)entry.getValue();

....

}

调用entrySet()的源码流程如下:

private transient Set<Map.Entry<K,V>> entrySet = null;   //定义变量

public Set<Map.Entry<K,V>> entrySet() {

return entrySet0();

}

private Set<Map.Entry<K,V>> entrySet0() {

Set<Map.Entry<K,V>> es = entrySet;

return es != null ? es : (entrySet = new EntrySet());

}

private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {

public Iterator<Map.Entry<K,V>> iterator() {

return newEntryIterator();

}

public boolean contains(Object o) {

if (!(o instanceof Map.Entry))

return false;

Map.Entry<K,V> e = (Map.Entry<K,V>) o;

Entry<K,V> candidate = getEntry(e.getKey());

return candidate != null && candidate.equals(e);

}

public boolean remove(Object o) {

return removeMapping(o) != null;

}

public int size() {

return size;

}

public void clear() {

HashMap.this.clear();

}

}

Iterator<Map.Entry<K,V>> newEntryIterator()   {

return new EntryIterator();

}

private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {

public Map.Entry<K,V> next() {

return nextEntry();

}

}

private abstract class HashIterator<E> implements Iterator<E> {

Entry<K,V> next;        // next entry to return

int expectedModCount;   // For fast-fail

int index;              // current slot

Entry<K,V> current;     // current entry

HashIterator() {

expectedModCount = modCount;

if (size > 0) { // advance to first entry

Entry[] t = table;

while (index < t.length && (next = t[index++]) == null);// 将index指向第一个table不为null的位置

}

}

public final boolean hasNext() {

return next != null;

}

final Entry<K,V> nextEntry() {    //查找下一个Entry

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

Entry<K,V> e = next;

if (e == null)

throw new NoSuchElementException();

if ((next = e.next) == null) {

Entry[] t = table;

while (index < t.length && (next = t[index++]) == null); // 如果遍历完一个Entry链,则继续查找下一个Entry链的索引

}

current = e;

return e;

}

public void remove() {

if (current == null)

throw new IllegalStateException();

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

Object k = current.key;

current = null;

HashMap.this.removeEntryForKey(k);

expectedModCount = modCount;

}

}

我们知道java.util.HashMap不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。

这一策略在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount

Java Map遍历方式的选择.

8.负载因子

loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/ 散列表的容量(m)

负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

  HashMap的实现中,通过threshold字段来判断HashMap的最大容量:threshold = (int)(capacity * loadFactor);

结合负载因子的定义公式可知,threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实际的负载因子(也就是说虽然数组长度是capacity,但其扩容的临界值确是threshold)。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。当容量超出此最大容量时, resize后的HashMap容量是容量的两倍。

9.例子(引用)--帮助理解底层数据结构得实现以及负载因子

我们有很多的小猪,每个的体重都不一样,假设体重分布比较平均(我们考虑到公斤级别),我们按照体重来分,划分成100个小猪圈。 然后把每个小猪,按照体重赶进各自的猪圈里,记录档案。 好了,如果我们要找某个小猪怎么办呢?我们需要每个猪圈,每个小猪的比对吗? 当然不需要了。 我们先看看要找的这个小猪的体重,然后就找到了对应的猪圈了。 在这个猪圈里的小猪的数量就相对很少了。 我们在这个猪圈里就可以相对快的找到我们要找到的那个小猪了。 对应于hash算法。 就是按照hashcode分配不同的猪圈,将hashcode相同的猪放到一个猪圈里。 查找的时候,先找到hashcode对应的猪圈,然后在逐个比较里面的小猪。 所以问题的关键就是建造多少个猪圈比较合适。 如果每个小猪的体重全部不同(考虑到毫克级别),每个都建一个猪圈,那么我们可以最快速度的找到这头猪。缺点就是,建造那么多猪圈的费用有点太高了。 如果我们按照10公斤级别进行划分,那么建造的猪圈只有几个吧,那么每个圈里的小猪就很多了。我们虽然可以很快的找到猪圈,但从这个猪圈里逐个确定那头小猪也是很累的。 所以,好的hashcode,可以根据实际情况,根据具体的需求,在时间成本(更多的猪圈,更快的速度)和空间本(更少的猪圈,更低的空间需求)之间平衡。

10.线程安全性

参考Java框架之CurrentHashMap

时间: 2024-11-10 18:09:54

Java集合框架之HashMap的相关文章

java集合框架08——HashMap和源码分析

本文为博主原创文章,转载请注明出处:http://blog.csdn.net/eson_15/article/details/51154989 上一章总体分析了Map架构,并简单分析了一下AbstractMap源码,这一章开始我们将对Map的具体实现类进行详细的学习.本章先研究HashMap.依然遵循以下步骤:先对HashMap有个整体的认识,然后学习它的源码,深入剖析HashMap. 1.HashMap简介 首先看一下HashMap的继承关系 java.lang.Object ? java.u

java集合框架12——HashMap和HashTable的区别

前面已经学习了Map的部分内容,主要是HashMap和HashTable,这一节我们来看看它们两有啥异同点. 1. HashMap和HashTable的相同点 HashMap和HashTable都是存储"键值对"的散列表,而且都是采用拉链法来实现的.存储的思想都是:通过table数组存储,数组的每个元素都是一个Entry,而一个Entry就是一个单项链表,Entry链表中的每个节点都保存了key-value数据. HashMap和HashTable的相同点基本没啥好研究的,我们更多的是

java集合框架小结(进阶版)之HashMap篇

基本概念: Hash(哈希):hash一般也译作“散列”.事实上,就是一个函数,用于直接定址.将数据元素的关键字key作为变量,通过哈希函数,计算生成该元素的存储地址. 冲突:函数是可以多对一的.即:多个自变量可以映射到同一函数值.一般而言,不同的key的hash值是不同的.在往hash表中映射的时候,不同的hash值可能映射到同一存储地址,这种情况被称为冲突. 解决冲突的方法: 1. 链表法:将冲突的各个元素用一个一维数组来维护.(java源码实现) 2. 开发寻址法:具体的有线性探测法.二次

java集合框架之java HashMap代码解析

 java集合框架之java HashMap代码解析 文章Java集合框架综述后,具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 源自http://www.codeceo.com/article/java-hashmap-java-collection.html 签名(signature) public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Se

Java—集合框架List

集合的概念 现实生活中:很多的事物凑在一起 数学中的集合:具有共同属性的事物的总和 Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象 集合的作用 在类的内部,对数据进行组织(针对作用与意义一样的属性,将他们放到一个集合中) 简单而快速的搜索大数量的条目 有的集合接口,提供了一系列排列有序的元素,并且可以在序列中快速的插入或删除有关元素 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型 与数组相比 数组的长度

《深入理解Java集合框架》系列文章

Introduction 关于C++标准模板库(Standard Template Library, STL)的书籍和资料有很多,关于Java集合框架(Java Collections Framework, JCF)的资料却很少,甚至很难找到一本专门介绍它的书籍,这给Java学习者们带来不小的麻烦.我深深的不解其中的原因.虽然JCF设计参考了STL,但其定位不是Java版的STL,而是要实现一个精简紧凑的容器框架,对STL的介绍自然不能替代对JCF的介绍. 本系列文章主要从数据结构和算法层面分析

JAVA集合框架

收藏 查看我的收藏 146有用+1 56 编辑 Java,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台的总称.用Java实现的HotJava浏览器(支持Java applet)显示了Java的魅力:跨平台.动态的Web.Internet计算.从此,Java被广泛接受并推动了Web的迅速发展,常用的浏览器现在均支持Java applet.集合框架是为表示和操作集合而规定的一种统一的标准的体系结构.任何集合框架都包含三大块内容:对外的接口.接口的实

我所理解Java集合框架的部分的使用(Collection和Map)

所谓集合,就是和数组类似--一组数据.java中提供了一些处理集合数据的类和接口,以供我们使用. 由于数组的长度固定,处理不定数量的数据比较麻烦,于是就有了集合. 以下是java集合框架(短虚线表示接口,长虚线表示抽象类,实线表示类,箭头表示实现接口或者继承)(在网络上找的图,不知道原作者,侵权请联系我删除)(总之,关系很复杂,所以不用记得这个图,只是用来吓吓人而已的). 下面贴上个人理解之精简版之Collection(集)和Map(地图?暂且这么理解吧),话说思维导图蛮好用,以下是两幅思维导图

2016.3.9-3.10(java集合框架)

3.9 集合框架有什么作用? 集合框架其主要功能是用来将存储的数据以某种结构组织,并以特定的方式来访问这些数据. Java集合框架中有哪些存储方式? Java集合框架中的对象容器,按照对象在其中的存储方式,分为Set.List.和Map三种类型. Set类型对象没有顺序,且不能重复: List类型对象容器中的对象按照索引顺序排序,而且可以有重复的对象: Map类型对象容器中的元素包含一对“键对象-值对象”映射,其中键对象不能重复,值对象可以重复. 以上三种存储方式对应Java集合框架中Set.L