Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读

Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读
Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读

Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读

什么是HashMap

HashMap 是一个采用哈希表实现的键值对集合,继承自AbstractMap,实现了Map 接口。

HashMap 的特殊存储结构使得在获取指定元素前需要经过哈希运算,得到目标元素在哈希表中的位置,然后再进行少量比较即可得到元素,这使得HashMap 的查找效率贼高。

当发生哈希冲突(碰撞)的时候,HashMap 采用拉链法进行解决(不熟悉“哈希冲突” 和“拉链法” 这2 个概念的同学可以点这里了解),因此HashMap 的底层实现是数组+链表,如下图所示:

HashMap 的特点

结合平时使用,可以了解到HashMap 大概具有以下特点:

?底层实现是链表数组,JDK 8 后又加了红黑树

?实现了Map 全部的方法

?key 用Set 存放,所以想做到key 不允许重复,key 对应的类需要重写hashCode 和equals 方法

?允许空键和空值(但空键只有一个,且放在第一位,下面会介绍)

?元素是无序的,而且顺序会不定时改变

?插入、获取的时间复杂度基本是O(1)(前提是有适当的哈希函数,让元素分布在均匀的位置)?遍历整个Map 需要的时间与桶(数组) 的长度成正比(因此初始化时HashMap 的容量不宜太大)

?两个关键因子:初始容量、加载因子

除了不允许null 并且同步,Hashtable 几乎和他一样。

下面结合源码进行验证这些特点。

HashMap 的13 个成员变量

1.默认初始容量:16,必须是2 的整数次方

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

2.默认加载因子的大小:0.75,可不是随便的,结合时间和空间效率考虑得到的

static final float DEFAULT_LOAD_FACTOR = 0.75f;

3.最大容量:2^ 30 次方

static final int MAXIMUM_CAPACITY = 1 << 30;

4.当前HashMap 修改的次数,这个变量用来保证fail-fast机制

transient int modCount;

5.阈值,下次需要扩容时的值,等于容量*加载因子

int threshold;

6.树形阈值:JDK 1.8 新增的,当使用树而不是列表来作为桶时使用。必须必2 大

static final int TREEIFY_THRESHOLD = 8;

7.非树形阈值:也是1.8 新增的,扩容时分裂一个树形桶的阈值(?不是很懂- -),要比

TREEIFY_THRESHOLD 小

static final int UNTREEIFY_THRESHOLD = 6;

8.树形最小容量:桶可能是树的哈希表的最小容量。至少是TREEIFY_THRESHOLD 的4 倍,这样能避免扩容时的冲突

static final int MIN_TREEIFY_CAPACITY = 64;

9.缓存的键值对集合(另外两个视图:keySet 和values 是在AbstractMap中声明的)

transient Set> entrySet;

10.哈希表中的链表数组

transient Node[] table;

11.键值对的数量

transient int size;

12.哈希表的加载因子

final float loadFactor;

HashMap 的初始容量和加载因子

由于HashMap 扩容开销很大(需要创建新数组、重新哈希、分配等等),因此与扩容相关的两个因素:?容量:数组的数量

?加载因子:决定了HashMap 中的元素占有多少比例时扩容

成为了HashMap 最重要的部分之一,它们决定了HashMap 什么时候扩容。

HashMap 的默认加载因子为0.75,这是在时间、空间两方面均衡考虑下的结果:

?加载因子太大的话桶太多,遍历时效率变低

?太小的话频繁rehash,导致性能降低

当设置初始容量时,需要提前考虑Map 中可能有多少对键值对,设计合理的加载因子,尽可能避免进行扩容。

如果存储的键值对很多,干脆设置个大点的容量,这样可以少扩容几次。

HashMap 的关键方法

1. HashMap 的4 个构造方法

//创建一个空的哈希表,初始容量为 16,加载因子为 0.75

public HashMap() {

this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted

}

//创建一个空的哈希表,指定容量,使用默认的加载因子

public HashMap(int initialCapacity) {

this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

//创建一个空的哈希表,指定容量和加载因子

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);

this.loadFactor = loadFactor;

//根据指定容量设置阈值

this.threshold = tableSizeFor(initialCapacity);

}

//创建一个内容为参数 m 的内容的哈希表

public HashMap(Map m) {

this.loadFactor = DEFAULT_LOAD_FACTOR;

putMapEntries(m, false);

}

其中第三种构造方法调用了tableSizeFor(int)来根据指定的容量设置阈值,这个方法经过若干次无符号右移、求异运算,得出最接近指定参数cap 的2 的N 次方容量。假如你传入的是5,返回的初始容量为8 。

static final int tableSizeFor(int cap) {

int n = cap - 1;

n |= n >>> 1;

n |= n >>> 2;

n |= n >>> 4;

n |= n >>> 8;

n |= n >>> 16;

return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }

第四种构造方法调用了putMapEntries(),这个方法用于向哈希表中添加整个集合:

final void putMapEntries(Map m, boolean evict) { int s = m.size();

if (s > 0) {

//数组还是空,初始化参数

if (table == null) {

float ft = ((float)s / loadFactor) + 1.0F;

int t = ((ft < (float)MAXIMUM_CAPACITY) ?

(int)ft : MAXIMUM_CAPACITY);

if (t > threshold)

threshold = tableSizeFor(t);

}

//数组不为空,超过阈值就扩容

else if (s > threshold)

resize();

for (Map.Entry e : m.entrySet()) {

K key = e.getKey();

V value = e.getValue();

//先经过 hash() 计算位置,然后复制指定 map 的内容

putVal(hash(key), key, value, false, evict);

}

}

2.HashMap 中的链表节点

前面提到,HashMap 的底层数据结构之一(JDK 1.8 前单纯是)就是链表数组:transient Node[] table;

每一个链表节点如下:

//实现了 Map.Entry 接口

static class Node implements Map.Entry {

//哈希值,就是位置

final int hash;

//键

final K key;

//值

V value;

//指向下一个几点的指针

Node next;

Node(int hash, K key, V value, Node next) {

this.hash = hash;

this.key = key;

this.value = value;

this.next = next;

}

public final K getKey() { return key; }

public final V getValue() { return value; }

public final String toString() { return key + "=" + value; }

public final int hashCode() {

return Objects.hashCode(key) ^ Objects.hashCode(value); }

public final V setValue(V newValue) {

V oldValue = value;

value = newValue;

return oldValue;

}

public final boolean equals(Object o) {

if (o == this)

return true;

if (o instanceof Map.Entry) {

//Map.Entry 相等的条件:键相等、值相等、个数相等、顺序相等

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

if (Objects.equals(key, e.getKey()) &&

Objects.equals(value, e.getValue()))

return true;

}

return false;

}

}

3.HashMap 中的添加操作**

下文用“桶”来指代要数组,每个桶都对应着一条链表:

//添加指定的键值对到 Map 中,如果已经存在,就替换

public V put(K key, V value) {

//先调用 hash() 方法计算位置

return putVal(hash(key), key, value, false, true);

}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

boolean evict) {

Node[] tab; Node p; int n, i;

//如果当前哈希表内容为空,新建,n 指向最后一个桶的位置,tab 为哈希表另一个引用

//resize() 后续介绍

if ((tab = table) == null || (n = tab.length) == 0)

n = (tab = resize()).length;

//如果要插入的位置没有元素,新建个节点并放进去

if ((p = tab[i = (n - 1) & hash]) == null)

tab[i] = newNode(hash, key, value, null);

else {

//如果要插入的桶已经有元素,替换

// e 指向被替换的元素

Node e; K k;

if (p.hash == hash &&

((k = p.key) == key || (key != null && key.equals(k))))

//p 指向要插入的桶第一个元素的位置,如果 p 的哈希值、键、值和要添加的一样,就停止找,e 指向 p

e = p;

else if (p instanceof TreeNode)

//如果不一样,而且当前采用的还是 JDK 8 以后的树形节点,调用 putTreeVal 插入

e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

else {

//否则还是从传统的链表数组查找、替换

//遍历这个桶所有的元素

for (int binCount = 0; ; ++binCount) {

//没有更多了,就把要添加的元素插到后面得了

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

p.next = newNode(hash, key, value, null);

//当这个桶内链表个数大于等于 8,就要树形化

if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st

treeifyBin(tab, hash);

break;

}

//如果找到要替换的节点,就停止,此时 e 已经指向要被替换的节点

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k)))) break;

p = e;

}

}

//存在要替换的节点

if (e != null) {

V oldValue = e.value;

//替换,返回

if (!onlyIfAbsent || oldValue == null)

e.value = value;

afterNodeAccess(e);

return oldValue;

}

}

++modCount;

//如果超出阈值,就得扩容

if (++size > threshold)

resize();

afterNodeInsertion(evict);

return null;

}

根据代码可以总结插入逻辑如下:

1.先调用hash() 方法计算哈希值

2.然后调用putVal() 方法中根据哈希值进行相关操作

3.如果当前哈希表内容为空,新建一个哈希表

4.如果要插入的桶中没有元素,新建个节点并放进去

5.否则从桶中第一个元素开始查找哈希值对应位置

1.如果桶中第一个元素的哈希值和要添加的一样,替换,结束查找

2.如果第一个元素不一样,而且当前采用的还是JDK 8 以后的树形节点,调用putTreeVal()

进行插入

3.否则还是从传统的链表数组中查找、替换,结束查找

4.当这个桶内链表个数大于等于8,就要调用treeifyBin() 方法进行树形化

6.最后检查是否需要扩容

插入过程中涉及到几个其他关键的方法:

?hash():计算对应的位置

?resize():扩容

?putTreeVal():树形节点的插入

treeifyBin():树形化容器

HashMap 在JDK1.8 新增树形化相关的内容比较多,下一篇介绍,接下来先介绍传统的HashMap 相关内容。

4.HashMap 中的哈希函数hash() **

HashMap 中通过将传入键的hashCode 进行无符号右移16 位,然后进行按位异或,得到这个键的哈希值。static final int hash(Object key) {

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

看看源码里的注释怎么说的:

Computes key.hashCode() and spreads (XORs) higher bits of hash to lower. Because the table uses power-of-two masking, sets of hashes that vary only in bits above the current mask will always collide. (Among known examples are sets of Float keys holding consecutive whole numbers in small tables.) So we apply a transform that spreads the impact of higher bits https://www.360docs.net/doc/7718968173.html,wnward. There is a tradeoff between speed, utility, and quality of

bit-spreading. Because many common sets of hashes are already reasonably distributed (so don’t benefit from spreading), and because we use trees to handle large sets of collisions in bins, we just XOR some shifted bits in the cheapest possible way to reduce systematic lossage, as well as to incorporate impact of the highest bits that would otherwise never be used in index calculations because of table bounds.

大概意思就是:

由于哈希表的容量都是2 的N 次方,在当前,元素的hashCode() 在很多时候下低位是相同的,这将导致冲突(碰撞),因此 1.8 以后做了个移位操作:将元素的hashCode() 和自己右移16 位后的结果求异或。由于int 只有32 位,无符号右移16 位相当于把高位的一半移到低位:

举个栗子:

这样可以避免只靠低位数据来计算哈希时导致的冲突,计算结果由高低位结合决定,可以避免哈希值分布不均匀。

而且,采用位运算效率更高。

5.HashMap 中的初始化/扩容方法resize() **

每次添加时会比较当前元素个数和阈值:

//如果超出阈值,就得扩容

if (++size > threshold)

resize();

扩容:

final Node[] resize() {

//复制一份当前的数据

Node[] oldTab = table;

//保存旧的元素个数、阈值

int oldCap = (oldTab == null) ? 0 : oldTab.length;

int oldThr = threshold;

int newCap, newThr = 0;

if (oldCap > 0) {

if (oldCap >= MAXIMUM_CAPACITY) {

threshold = Integer.MAX_VALUE;

return oldTab;

}

//新的容量为旧的两倍

else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&

oldCap >= DEFAULT_INITIAL_CAPACITY)

//如果旧容量小于等于 16,新的阈值就是旧阈值的两倍

newThr = oldThr << 1; // double threshold

}

//如果旧容量为 0 ,并且旧阈值>0,说明之前创建了哈希表但没有添加元素,初始化容量等于阈值 else if (oldThr > 0) // initial capacity was placed in threshold

newCap = oldThr;

else { // zero initial threshold signifies using defaults

//旧容量、旧阈值都是0,说明还没创建哈希表,容量为默认容量,阈值为容量*加载因子

newCap = DEFAULT_INITIAL_CAPACITY;

newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

}

//如果新的阈值为 0 ,就得用新容量*加载因子重计算一次

if (newThr == 0) {

float ft = (float)newCap * loadFactor;

newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);

}

//更新阈值

threshold = newThr;

//创建新链表数组,容量是原来的两倍

@SuppressWarnings({"rawtypes","unchecked"})

Node[] newTab = (Node[])new Node[newCap];

table = https://www.360docs.net/doc/7718968173.html,Tab;

//接下来就得遍历复制了

if (oldTab != null) {

for (int j = 0; j < oldCap; ++j) {

Node e;

if ((e = oldTab[j]) != null) {

//旧的桶置为空

oldTab[j] = null;

//当前桶只有一个元素,直接赋值给对应位置

if (e.next == null)

newTab[e.hash & (newCap - 1)] = e;

else if (e instanceof TreeNode)

//如果旧哈希表中这个位置的桶是树形结构,就要把新哈希表里当前桶也变成树形结构

((TreeNode)e).split(this, newTab, j, oldCap);

else { //保留旧哈希表桶中链表的顺序

Node loHead = null, loTail = null;

Node hiHead = null, hiTail = null;

Node next;

//do-while 循环赋值给新哈希表

do {

next = e.next;

if ((e.hash & oldCap) == 0) {

if (loTail == null)

loHead = e;

else

loTail.next = e;

loTail = e;

}

else {

if (hiTail == null)

hiHead = e;

else

hiTail.next = e;

hiTail = e;

}

} while ((e = next) != null);

if (loTail != null) {

loTail.next = null;

newTab[j] = loHead;

}

if (hiTail != null) {

hiTail.next = null;

newTab[j + oldCap] = hiHead;

}

}

}

}

}

return newTab;

}

扩容过程中几个关键的点:

?新初始化哈希表时,容量为默认容量,阈值为容量*加载因子

?已有哈希表扩容时,容量、阈值均翻倍

?如果之前这个桶的节点类型是树,需要把新哈希表里当前桶也变成树形结构

?复制给新哈希表中需要重新索引(rehash),这里采用的计算方法是

o e.hash & (newCap - 1),等价于e.hash % newCap

结合扩容源码可以发现扩容的确开销很大,需要迭代所有的元素,rehash、赋值,还得保留原来的数据结构。所以在使用的时候,最好在初始化的时候就指定好HashMap 的长度,尽量避免频繁resize()。

6.HashMap 的获取方法get() **

HashMap 另外一个经常使用的方法就是get(key),返回键对应的值:

如果HashMap 中包含一个键值对k-v 满足:

(key == null ? k == null : key.equals(k))

就返回值v,否则返回null;

public V get(Object key) {

Node e;

//还是先计算哈希值

return (e = getNode(hash(key), key)) == null ? null : e.value;

}

final Node getNode(int hash, Object key) {

Node[] tab; Node first, e; int n; K k;

//tab 指向哈希表,n 为哈希表的长度,first 为 (n - 1) & hash 位置处的桶中的头一个节点 if ((tab = table) != null && (n = tab.length) > 0 &&

(first = tab[(n - 1) & hash]) != null) {

//如果桶里第一个元素就相等,直接返回

if (first.hash == hash &&

((k = first.key) == key || (key != null && key.equals(k))))

return first;

//否则就得慢慢遍历找

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

if (first instanceof TreeNode)

//如果是树形节点,就调用树形节点的 get 方法

return ((TreeNode)first).getTreeNode(hash, key);

do {

//do-while 遍历链表的所有节点

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

return e;

} while ((e = e.next) != null);

}

}

return null;

}

查找方法比较简单:

?先计算哈希值;

?然后再用(n - 1) & hash计算出桶的位置;

?在桶里的链表进行遍历查找。

时间复杂度一般跟链表长度有关,因此哈希算法越好,元素分布越均匀,get() 方法就越快,不然遍历一条长链表,太慢了。

不过在JDK 1.8 以后HashMap 新增了红黑树节点,优化这种极端情况下的性能问题。

总结

1.HashMap 有那么多优点(见文首),也有个缺点:不是同步的。

当多线程并发访问一个哈希表时,需要在外部进行同步操作,否则会引发数据不同步问题。

你可以选择加锁,也可以考虑用Collections.synchronizedMap包一层,变成个线程安全的Map:Map m = Collections.synchronizedMap(new HashMap(...));

最好在初始化时就这么做。

Java集合类知识点总结

Java集合类 Java集合类 (1) 1.Map (3) 1.1.HashMap (3) 1.1.1.底层实现 (3) 1.1.2.特点 (3) 1.1.3.源码分析 (4) 1.1.4.多线程可能出现的问题 (5) 1.2.ConcurrentHashMap (6) 1.2.1.底层实现 (6) 1.2.2.源码分析 (7) 1.3.HashTable (9) 1.3.1.HashTable是线程安全的,因为所有方法上都加了synchronized关键 字。9 1.3.2.HashTable的key和value都不可以为null。 (9) 1.3.3.扩容时,capacity=2*capacity+1 (9) 1.3.4.数组默认大小为11 (9) 1.3.5.查找下标时,没有使用hash&length-1,而是直接进行计算的 (9) 1.4.TreeMap (9) 1.4.1.底层实现为红黑树 (9) 1.4. 2.TreeMap是一个有序的key-value集合,基于红黑树实现。该映射根据 其键的自然顺序进行排序,或者根据创建时提供的Comparator进行排序 (10) 1.4.3.接口实现 (10) 1.4.4.Entry (11) 1.5.LinkedHashMap (11) 1.5.1.底层是数组+链表+红黑树+双向链表 (11) 1.5.2.维护链表顺序和访问顺序 (11) 1.5.3.LinkedHashMap 可以通过构造参数 accessOrder 来指定双向链表是否在 元素被访问后改变其在双向链表中的位置。 (11) 1.5.4.当accessOrder为true时,get方法和put方法都会调用recordAccess 方法使得最近使用的Entry移到双向链表的末尾;当accessOrder为默认值 false时,recordAccess方法什么也不会做。 (11) 1.5.5.LRU实现 (11) 2.Collection (11) 2.1.List (12) 2.1.1.ArrayList (12) 2.1.2.LinkedList (13) 2.1.3.CopyOnWriteArrayList (13) 2.2.Set (14) 2.2.1.HashSet (14)

Java Map集合

java.util 中的集合类包含Java 中某些最常用的类。最常用的集合类是List 和Map。List 的具体实现包括ArrayList 和V ector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。List 适用于按数值索引访问元素的情形。 Map 提供了一个更通用的元素存储方法。Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。从概念上而言,您可以将List 看作是具有数值键的Map。而实际上,除了List 和Map 都在定义java.util 中外,两者并没有直接的联系。本文将着重介绍核心Java 发行套件中附带的Map,同时还将介绍如何采用或实现更适用于您应用程序特定数据的专用Map。 了解Map 接口和方法 Java 核心类中有很多预定义的Map 类。在介绍具体实现之前,我们先介绍一下Map 接口本身,以便了解所有实现的共同点。Map 接口定义了四种类型的方法,每个Map 都包含这些方法。下面,我们从两个普通的方法(表1)开始对这些方法加以介绍。 表1:覆盖的方法。我们将这Object 的这两个方法覆盖,以正确比较Map 对象的等价性。 Map 构建 Map 定义了几个用于插入和删除元素的变换方法(表2)。 表2:Map 更新方法:可以更改Map 内容。 尽管您可能注意到,纵然假设忽略构建一个需要传递给putAll() 的Map 的开销,使用putAll() 通常也并不比使用大量的put() 调用更有效率,但putAll() 的存在一点也不稀奇。这是因为,putAll() 除了迭代put() 所执行的将每个键值对添加到Map 的算法以外,还需要迭代所传递的Map 的元素。但应注意,putAll() 在添加所有元素之前可以正确调整Map 的大小,因此如果您未亲自调整Map 的大小(我们将对此进行简单介绍),则putAll() 可能比预期的更有效。 查看Map 迭代Map 中的元素不存在直接了当的方法。如果要查询某个Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该Map 的“视图”。有三种可能的视图(参见表3) 所有键值对—参见entrySet() 所有键—参见keySet() 所有值—参见values() 前两个视图均返回Set 对象,第三个视图返回Collection 对象。就这两种情况而言,问题到这里并没有结束,这是因为您无法直接迭代Collection 对象或Set 对象。要进行迭代,

java中Map类

java中Map类 Map以按键/数值对的形式存储数据,和数组非常相似,在数组中存在的索引,它们本身也是对象。 Map的接口 Map---实现Map Map.Entry--Map的内部类,描述Map中的按键/数值对。 SortedMap---扩展Map,使按键保持升序排列 关于怎么使用,一般是选择Map的子类,而不直接用Map类。 下面以HashMap为例。 public static void main(String args[]) { HashMap hashmap = new HashMap(); hashmap.put("Item0", "Value0"); hashmap.put("Item1", "Value1"); hashmap.put("Item2", "Value2"); hashmap.put("Item3", "Value3"); Set set = hashmap.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext() { Map.Entry mapentry = (Map.Entry) iterator.next(); System.out.println(mapentry.getkey() + "/" + mapentry.getValue()); } } 注意,这里Map的按键必须是唯一的,比如说不能有两个按键都为null。 如果用过它,就会知道它的用处了。 又比如: Map map = new HashMap(); map.put("Order", (Order) obj); 资料: Collection容器中包含Set和List接口,Set中又包含HashSet,List中包含LinkedList和ArrayList;单独的Map接口中只有HashMap。 java.util 中的集合类包含Java 中某些最常用的类。最常用的集合类是List 和Map。List 的具体实现包括ArrayList 和Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。List 适用于按数值索引访问元素的情形,其中的数据有顺序且可以重复。而Set中数据无顺序且不可以重复。

JAVA中常用的集合类型

JAVA常用的高级数据类型——集合类型 一、JAVA语言中的集合类型主要有三种形式:Set(集)、List(列表)、Map(映射),每种类型的集合都包括三部分:接口、实现和算法。 a)集合接口实现集合的操作和集合的具体功能实现细节的相互分离—— Set接口、List接口、Map接口 b)集合的具体功能实现类实质上是各种可重用的数据结构的具体表示 List接口的实现类有ArrayList、LinkedList、Stack和Vector等 集合类,Vector 类提供了实现可增长数组的功能,随着更多元素加 入其中,数组变的更大。在删除一些元素之后,数组变小。 Set接口的实现类有HashSet、LinkedHashSet和TreeSet等集合类 Map接口的实现类有HashMap、Hashtable、LinkedHashMap、Properties和TreeMap等集合类。 c)集合的算法指可以对实现集合接口的各个集合的功能实现类提供如排 序、查找、交换和置换等方面的功能实现。 二、List接口 1.List接口代表有序的集合,可以对List接口代表的有序集合中每个元素 的插入位置进行精确地控制,并利用元素的整数索引(代表元素在集合中的位置)访问元素中的各个成员,List接口代表的集合是允许出现重复元素的。 2.List接口主要成员方法: 1)void add(int index,E element)在列表指定位置插入指定元素 2)E get(int index) 返回结合中指定位置的元素 3)E remove(int index) 移除集合中指定位置的元素 4)E set(int index,E elment) 用指定元素替换集合中指定位置的元素 5)boolean add(E o) 向列表的尾部追加指定的元素 6)boolean contains(Object o) 如果列表包含指定的元素,则返回true。 7)boolean isEmpty() 如果列表不包含元素,则返回 true。 8)int size() 返回列表中的元素数 9)Iterator iterator()返回以正确顺序在列表的元素上进行迭代的迭代器。 3.List的实现类 List在数据结构中分别表现为数组(ArrayList)、向量(Vector)、链表(LinkedList)、堆栈(Stack)和队列等形式。 Vector集合和ArrayList集合都是采用数组形式来保存对象,区别在于ArrayList集合本身不具有线程同步的特性,不能用在多线程的环境下,可以使用ArrayList集合能够节省由于同步而产生的系统性能的开销。而Vector集合实现了对线程同步的支持,因此在多线程并发访问的应用环境下,该集合本身能够保证自身具有线程安全性。在多线程的并发访问中,可以将Vector集合的对象实例设计为类中的成员属性,而应该将ArrayList 集合的对象实例设计为局部对象。 public class UserInfo{ List oneVector=new Vector(); public void execute(){

集合里Map,Set,List的区别

Java中的Set,List,Map的区别 (转) 对JAVA的集合的理解是相对于数组 相对于数组的是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型) JAVA集合可以存储和操作数目不固定的一组数据。 所有的JAVA集合都位于 java.util包中! JAVA集合只能存放引用类型的的数据,不能存放基本数据类型. JAVA集合主要分为三种类型: Set(集) List(列表) Map(映射) Collection 接口 Collection是最基本的集合接口,声明了适用于JAVA集合(只包括Set和List)的通用方法。 Set 和List 都继承了Conllection,Map没有 Collection接口的方法: boolean add(Object o) :向集合中加入一个对象的引用 void clear() :删除集合中所有的对象,即不再持有这些对象的引用 boolean isEmpty() :判断集合是否为空 boolean contains(Object o): 判断集合中是否持有特定对象的引用 Iterartor iterator() : 返回一个Iterator对象,可以用来遍历集合中的元素 boolean remove(Object o):从集合中删除一个对象的引用 int size() :返回集合中元素的数目 Object[] toArray() :返回一个数组,该数组中包括集合中的所有元素 关于:Iterator() 和toArray() 方法都用于集合的所有的元素,前者返回一个Iterator对象,后者返回一个包含集合中所有元素的数组。 Iterator接口声明了如下方法: hasNext(): 判断集合中元素是否遍历完毕,如果没有,就返回true next() :返回下一个元素 remove():从集合中删除上一个有next()方法返回的元素。 Set(集合): Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set接口主要实现了两个实现类: HashSet : HashSet类按照哈希算法来存取集合中的对象,存取速度比较快

java集合类

Java集合类 Java集合类是一种特别有用的工具类,可用于存储数量不等的对象,并可以实现常用的数据结构,如栈、队列等;除此之外java集合还可用于保存具有映射关系的关联数组。 Java集合大致可以分为Set、List、Quequ和Map四种体系: Set代表无序、不可重复的集合; List代表有序、重复的集合; Map代表具有映射关系的集合; Queue体系集合,代表一种队列集合 Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称为对象)“丢进”该容器中。在java5之前,java集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型出来;从java5增加泛型以后,java集合可以记住容器中对象的数据类型,从而可以编写出更简洁、健壮的代码。 为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),java提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。 集合类和数组不一样,数组元素既可以基本类型的值,也可以是对象(实际上保存的是对象的引用变量);而集合里只能保存对象(保存对象的引用变量)。 Java的集合类主要由两个接口派生而出:Collection 和Map,Collection和Map是java集合框架的根接口,这两个接口又包含了一些子接口或实现类。 Set和list接口是collection接口派生的两个子接口,它们分别代表了无序集合和有序集合;Queue是java提供的队列实现。 Map实现类用于保存具有映射关系的数据。Map保存的每项数据都是key-value对,也就是由key和value两个值组成。(就像成绩单,每项成绩都是由两个值组成,即科目名和成绩。对于一张成绩表,科目通常不会重复,而成绩是可重复的,通常习惯根据科目来查阅成绩,而不会根据成绩来查阅科目)map里key是不可以重复的,key用于标识集合里的每项数据,如果需要查阅map中的数据时,总是根据map的key来获取。 Set,list,queue,map这4个接口,可以把java所有集合分成三大类,其中set集合类似于一个罐子,把一个对象添加到set 集合时,set集合无法记住添加这个元素的顺序,所以set里的元素不能重复(否则系统无法准确识别这个元素);list集合非常像一个数组,它可以记住每次添加元素的顺序、且list的长度可变。Map集合也像一个罐子,只是它里面的每项数据都是由两个值组成。 如果访问list集合中的元素,可以直接根据元素的索引来访问;如果访问map集合中的元素,可以根据每项元素的key来访问其value;如果访问set集合中的元素,则只能根据元素本身来访问(这也是set集合里元素不允许重复的原因) 对于set、list、Queue和map四种集合,最常用的实现类分别是HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList和HashMap、TreeMap等实现类。 Collection的用法有:添加元素、删除元素、返回collection集合中的元素个数以及清空整个集合等。Collection实现类都重写了toString()方法,该方法可以一次性的输出集合中的所有元素。 Java8为Iterable接口新增了一个forEach(Consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Iterable 接口是Collection接口的父接口,因此Collection集合也可以直接调用该方法。 当程序调用Iterable的forEach(Consumer action)遍历集合元素时,程序会一次将集合元素传给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。正因为Consumer是函数式接口,因此可以使用lambda表达式来遍历集合元素。

java集合类总结

1.本讲内容:集合collection (数组和集合90%功能相似,但是最大的区别是,数组在初始化的时候必须确定大小,而集合不用,而且集合是一堆的类,使用起来非常方便。) 讲集合collection之前,我们先分清三个概念: 1colection 集合,可用来存储任何对象的一种数据结构(容器)。 2Collection 集合接口,指的是,是Set、List 和Queue 接口的超类接口 3Collections 集合工具类,指的是类。 SCJP考试要求了解的接口有:Collection , Set , SortedSet , List , Map , SortedMap , Queue , NavigableSet , NavigableMap, 还有一个Iterator 接口也是必须了解的。 SCJP考试要求了解的类有:HashMap , Hashtable ,TreeMap , LinkedHashMap , HashSet , LinkedHashSet ,TreeSet , ArrayList , Vector , LinkedList , PriorityQueuee , Collections , Arrays 下面给出一个集合之间的关系图: 上图中加粗线的ArrayList 和HashMap 是我们重点讲解的对象。下面这张图看起来层级结构更清晰些。 我们这里说的集合指的是小写的collection,集合有4种基本形式,其中前三种的父接口是Collection。 4List 关注事物的索引列表 5Set 关注事物的唯一性 6Queue 关注事物被处理时的顺序 7Map 关注事物的映射和键值的唯一性 一、Collection 接口 Collection接口是Set 、List 和Queue 接口的父接口,提供了多数集合常用的方法声明,包括add()、remove()、contains() 、size() 、iterator() 等。 add(E e) 将指定对象添加到集合中 remove(Object o) 将指定的对象从集合中移除,移除成功返回true,不成功返回false contains(Object o) 查看该集合中是否包含指定的对象,包含返回true,不包含返回flase size() 返回集合中存放的对象的个数。返回值为int clear() 移除该集合中的所有对象,清空该集合。 iterator() 返回一个包含所有对象的iterator对象,用来循环遍历 toArray() 返回一个包含所有对象的数组,类型是Object toArray(T[] t) 返回一个包含所有对象的指定类型的数组 我们在这里只举一个把集合转成数组的例子,因为Collection本身是个接口所以,我们用它的实现类ArrayList做这个例子:例子1: package edu.xjfu;

java_Set,List,Map,Vector,ArrayList的区别

JAVA的容器---List,Map,Set Collection ├List │├LinkedList │├ArrayList │└Vector │└Stack └Set Map ├Hashtable ├HashMap └WeakHashMap Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。 所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。 如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下: Iterator it = collection.iterator(); // 获得一个迭代子 while(it.hasNext()) { Object obj = it.next(); // 得到下一个元素 } 由Collection接口派生的两个接口是List和Set。

Java中集合类用法总结

帮助 | 留言交? | 登录 首页我的图书馆主题阅读精彩目录精品文苑Tags 会员浏览好书推荐 以文找文 如何对文章标记,添加批注? Java 中集合?用法总结(转载) wade0564 收录于2010-07-08 阅读数:查看 收藏数:7 公众公开 原文来源 tags : java 集合类 欢迎浏览 wade0564 个人图书馆中收藏的文章,想收藏这篇好文章吗,赶快 吧,1分钟拥有自己的个人图书馆! 我也要收藏 举报 Java 中集合?用法总结 收藏 Collection ├List │├LinkedList │├ArrayList (异步,线程不安全,空间用完时自动增长原容量一半)│└Vector (同 步,线程安全,空间用完时自动增长原容量一倍)│ └Stack └Set ├HashSet └TreeSet Map ├Hashtable ├HashMap ├WeakHashMap └TreeMap Map 接口: | + -- WeakHashMap: 以弱键 实现的基于哈希表的 Map 。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条 | 目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为 可终止的,被终 | 止,然后被回收。丢弃某个键时, 其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。此实现 | 不是同步的。 | + -- TreeMap:该映射根据其键的自然顺序进行 排序,或?根据创建映射时提供的 Comparator 进行 排序,具体取决于使用的 | 构造方法。此实现不是同步的。 | + -- HashMap:基于哈希表的 Map 接?的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了 | 非同步和允许 使用 null 之外,HashMap 类与 Hashtable ?致相同。)此类不保证映射的顺序,特别是它不保证该顺 | 序恒久不变。此实现不是同步的。 | +-- SortedMap: 进一步提供关于键的总体排序 的 Map 。该映射是根据其键的自然顺序进 行排序的,或?根据通常在创建有 序映射时提供的 Comparator 进行排序。对有序映射的 collection 视图(由 entrySet 、keySet 和 values 方法返回 )进行迭代时,此顺序就会反映 出来。要采用此排序方式,还需要提供一些其他操作(此接?是 SortedSet 的对应映 射)。 Collection 接口: | 热点推荐 中国经典汤品——广东汤常用多音字汇总 如果你失恋。。。这些话...影响世界的100个管理定律汽车发动机?作过程和原理分析温家宝总理答中外记?问女人味,有多少男人可以读懂?珍稀的白头叶猴(组图)三鹿门事件之——中国,...国家公务员职务与级别当代古筝四美 付娜《渔...生活?秘方 真的很实用...哲理?品:守护梦想聚会时可以玩的?游戏依赖型人格障碍的表现和治疗经典妙语,十分精彩江边施救[贴图]李一男2003年在港湾...电脑速度慢的解决方法 ...重装系统后必须做的10件?事

java中List的用法

java中List的用法和实例详解 List的用法 List包括List接口以及List接口的所有实现类。因为List接口实现了Collection接口,所以List接口拥有Collection接口提供的所有常用方法,又因为List是列表类型,所以List接口还提供了一些适合于自身的常用方法,如表1所示。 表1 List接口定义的常用方法及功能 从表1可以看出,List接口提供的适合于自身的常用方法均与索引有关,这是因为List集合为列表类型,以线性方式存储对象,可以通过对象的索引操作对象。 List接口的常用实现类有ArrayList和LinkedList,在使用List集合时,通常情况下声明为List类型,实例化时根据实际情况的需要,实例化为ArrayList或LinkedList,例如: List l = new ArrayList();// 利用ArrayList类实例化List集合 List l2 = new LinkedList();// 利用LinkedList类实例化List集合 1.add(int index, Object obj)方法和set(int index, Object obj)方法的区别 在使用List集合时需要注意区分add(int index, Object obj)方法和 set(int index, Object obj)方法,前者是向指定索引位置添加对象,而后者是修改指定索引位置的对象,例如执行下面的代码: src\com\mwq\TestCollection.java关键代码: public static void main(String[] args) { String a = "A", b = "B", c = "C", d = "D", e = "E"; List list = new LinkedList(); list.add(a);

Java中的集合类

Java中的集合类 (Collection framework)我们在前面学习过java数组,java数组的程度是固定的,在同一个数组中只能存放相同的类型数据。数组可以存放基本类型的数据,也可以存入对象引用的数据。 在创建数组时,必须明确指定数组的长度,数组一旦创建,其长度就不能改变,在许多应用的场合,一组数据的数目不是固定的,比如一个单位的员工数目是变化的,有老的员工跳槽,也有新的员工进来。 为了使程序方便地存储和操纵数目不固定的一组数据,JDK中提供了java集合类,所有java集合类都位于java.util包中,与java数组不同,java集合类不能存放基本数据类型数据,而只能存放对象的引用。 Java集合类分为三种 Set(集合):集合中对象不按特定的方式排序。并且没有重复对象,但它有些实现类中的对象按特定方式排序。--无序,不能重复 List(列表):集合中的对象按照检索位置排序,可以有重复对象,允许按照对象在集中的索引位置检索对象,List和数组有些相似。--有序,可以重复 Map(映射):集合中的每一个元素包含一对键对象和值对象,集合中没有重复的键对象,值对象可以重复,它的有些实现类能对集合中的键对象进行排序。 Java的主要集合类的框架图 Collection和Iterator接口 在Collection接口中声明了适用于java集合(只包括Set和List)通用方法。 Collection接口的方法 方法描述

boolean add(Object o) 向集合中加入一个对象的引用 void clear( ) 删除集合中所有对象,即不再对持有对象的引用boolean contains(Object o) 判断在集合中是否含有特定对象的引用 boolean isEmpty() 判断集合是否为空 Iterator iterator( ) 返回一个Iterator对象,可用它来遍历集合中的元素boolean remove(Object o) 从集合中删除一个对象的引用 int size( ) 返回集合中元素的数目 Object [ ] toArray() 返回一个数组,该数组包含集合中的所有元素 Set接口和List即可都继承了Collection接口,而Map接口没有继承Collection接口,因此可以对Set对象和List对象调用以上方法,但是不能对Map对象调用以上方法。Collection接口的iterator()和toArray()方法多用于获得集合中的所有元素,前者返回一个Iterator对象,后者返回一个包含集合中所有元素的数组。 Iterator隐藏底层集合的数据结构,向客户程序提供了遍历各种类型的集合的统一接口。Iterator接口中声明了如下方法: ●hasNext():判断集合中的元素是否遍历完毕,如果没有,就返回true。 ●next():返回下一个元素 ●remove():从集合中删除上一个由next()方法返回的元素。 注意:如果集合中的元素没有排序,Iterator遍历集合中元素的顺序是任意的,并不一定与像集合中加入的元素的顺序一致。 Set(集) Set是最简单的一种集合,集合中的对象不按特定方式排序,并没有重复对象。Set接口主要有两个实现类:HashSet类还有一个子类LinkedHashSet类,它不仅实现了哈希算法,而且实现了链表数据结构,链表数据结构能提高插入核算出元素的性能。TreeSet类实现了SortedSet接口中,具有排序功能。 List(列表) List的主要特征使其元素已先行方式存储,集合中允许存放重复对象。List接口主要的实现类包括: ●ArrayList—ArrayList代表长度可变的数组。允许对元素进行快速的随机访问,但是向 ArrayList中插入与删除元素的速度较慢。 ●LinkedList—在实现中采用链表数据结构。对顺序访问进行了优化,向List中插入和 删除元素的速度较快,随机访问速度则相对较慢,随机访问是指检索位于特定索引位置元素。 Map(映射) Map(映射)是一种吧键对和值对象进行映射的集合。它的每一个元素都包含一对键对象和值对象,而之对象仍可以是Map类型。以此类推,这样就形成了多级映射。向Map集合中加入元素时,必须提供一对键对象和值对象,从Map集合上检索元素只要给出键对象,就会返回值对象。 实例1 CollectionAll.java

java集合-练习题解析

1. 填空 Collection 接口的特点是元素是___无序可重复______; List 接口的特点是元素__有__(有|无)顺序,_可以___(可以|不可以)重复; Set 接口的特点是元素__无___(有|无)顺序,____不可以__(可以|不可以)重复;Map 接口的特点是元素是__key、value映射______,其中__value__可以重复,_key___不可以重复。 2. (List)有如下代码 import java.util.*; public class TestList{ public static void main(String args[]){ List list = new ArrayList(); list.add(“Hello”); list.add(“World”); list.add(1, “Learn”); list.add(1, “Java”); printList(list); } public static void printList(List list){ //1 for(int i = 0; i< list.size();i++){ System.out.println(list.get(i)); } for(Object o : list) { System.out.println(o); } Iterator itor = list.iterator(); while(itor.hasNext()){ System.out.println(itor.next()); } } } 要求: 1) 把//1 处的代码补充完整,要求输出list 中所有元素的内容 2) 写出程序执行的结果Hello Java Learn World 3) 如果要把实现类由ArrayList 换为LinkedList,应该改哪里?ArrayList 和LinkedList 使用上有什么区别?实现上有什么区别? 4) 如果要把实现类由ArrayList 换为Vector,应该改哪里?ArrayList 和Vector 使 用上有什么区别?实现上有什么区别? 3. (List)写出下面程序的运行结果

Java编程题集合(二)

Java编程题集合(二) 【程序21】 题目:求1+2!+3!+...+20!的和 public class lianxi21 { public static void main(String[] args) { long sum = 0; long fac = 1; for(int i=1; i<=20; i++) { fac = fac * i; sum += fac; } System.out.println(sum); } }

【程序22】 题目:利用递归方法求5!。 public class lianxi22 { public static void main(String[] args) { int n = 5; rec fr = new rec(); System.out.println(n+"! = "+fr.rec(n)); } } class rec{ public long rec(int n) { long value = 0 ; if(n ==1 ) { value = 1; } else { value = n * rec(n-1); } return value; } }

【程序23】 题目:有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大? public class lianxi23 { public static void main(String[] args) { int age = 10; for(int i=2; i<=5; i++) { age =age+2; } System.out.println(age); } }

java中HashMap详解

java中HashMap详解 HashMap 和HashSet 是Java Collection Framework 的两个重要成员,其中HashMap 是Map 接口的常用实现类,HashSet 是Set 接口的常用实现类。虽然HashMap 和HashSet 实现的接口规范不同,但它们底层的Hash 存储机制完全一样,甚至HashSet 本身就采用HashMap 来实现的。 通过HashMap、HashSet 的源代码分析其Hash 存储机制 实际上,HashSet 和HashMap 之间有很多相似之处,对于HashSet 而言,系统采用Hash 算法决定集合元素的存储位置,这样可以保证能快速存、取集合元素;对于HashMap 而言,系统key-value 当成一个整体进行处理,系统总是根据Hash 算法来计算key-value 的存储位置,这样可以保证能快速存、取Map 的key-value 对。 在介绍集合存储之前需要指出一点:虽然集合号称存储的是Java 对象,但实际上并不会真正将Java 对象放入Set 集合中,只是在Set 集合中保留这些对象的引用而言。也就是说:Java 集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的Java 对象。 集合和引用 就像引用类型的数组一样,当我们把Java 对象放入数组之时,并不是真正的把Java 对象放入数组中,只是把对象的引用放入数组中,每个数组元素都是一个引用变量。

HashMap 的存储实现 当程序试图将多个key-value 放入HashMap 中时,以如下代码片段为例: Java代码 1. HashMap map = new HashMap(); 2. map.put("语文" , 80.0); 3. map.put("数学" , 89.0); 4. map.put("英语" , 78.2); HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置。 当程序执行map.put("语文" , 80.0); 时,系统将调用"语文"的hashCode() 方法得到其hashCode 值——每个Java 对象都有hashCode() 方法,都可通过该方法获得它的hashCode 值。得到这个对象的hashCode 值之后,系统会根据该hashCode 值来决定该元素的存储位置。 我们可以看HashMap 类的put(K key , V value) 方法的源代码: Java代码 public V put(K key, V value) { // 如果key 为null,调用putForNullKey 方法进行处理

Java集合框架的知识总结

Java集合框架的知识总结 说明:先从整体介绍了Java集合框架包含的接口和类,然后总结了集合框架中的一些基本知识和关键点,并结合实例进行简单分析。 1、综述 所有集合类都位于java.util包下。集合中只能保存对象(保存对象的引用变量)。(数组既可以保存基本类型的数据也可以保存对象)。 当我们把一个对象放入集合中后,系统会把所有集合元素都当成Object类的实例进行处理。从JDK1.5以后,这种状态得到了改进:可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型(参见具体泛型的内容)。 Java的集合类主要由两个接口派生而出:Collection和Map,Col lection和Map是Java集合框架的根接口,这两个接口又包含了一些接口或实现类。

Set和List接口是Collection接口派生的两个子接口,Queue是Java提供的队列实现,类似于List。 Map实现类用于保存具有映射关系的数据(key-value)。 Set、List和Map可以看做集合的三大类。 List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。 Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是不能集合里元素不允许重复的原因)。 Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的k ey来访问其value。 对于Set、List和Map三种集合,最常用的实现类分别是HashSet、ArrayLi st和HashMap三个实现类。(并发控制的集合类,以后有空研究下)。2、Collection接口 Collection接口是List、Set和Queue接口的父接口,同时可以操作这三个接口。Collection接口定义操作集合元素的具体方法大家可以参考API文档,这里通过一个例子来说明Collection的添加元素、删除元素、返回集合中元素的个数以及清空集合元素的方法。 public class TestCollection {

Java集合排序及java集合类详解

Java集合排序及java集合类详解(Collection, List, Set, Map) 摘要内容 Java里面最重要,最常用也就是集合一部分了。能够用好集合和理解好集合对于做Java程序的开发拥有无比的好处。本文详细解释了关于Java中的集合是如何实现的,以及他们的实现原理。 关键字: Collectio n , List ,Set , Map , 集合,框架。 目录 1 集合框架 (2) 1.1 集合框架概述 (2) 1.1.1 容器简介 (2) 1.1.2 容器的分类 (4) 1.2 Collec tio n (6) 1.2.1 常用方法 (6) 1.2.2 迭代器 (8) 1.3 List (10) 1.3.1 概述 (10) 1.3.2 常用方法 (11) 1.3.3 实现原理 (15) 1.4 Map (18) 1.4.1 概述 (18) 1.4.2 常用方法 (18) 1.4.3 Comparable接口 (23) 1.4.4 实现原理 (25) 1.4.5 覆写hashCode() (29) 1.5 Set (33) 1.5.1 概述 (33) 1.5.2 常用方法 (34) 1.5.3 实现原理 (38) 1.6 总结:集合框架中常用类比较 (39) 2 练习 (40) 3 附录:排序 (41)

1集合框架 1.1集合框架概述 1.1.1容器简介 到目前为止,我们已经学习了如何创建多个不同的对象,定义了这些对象以后,我们就可以利用它们来做一些有意义的事情。 举例来说,假设要存储许多雇员,不同的雇员的区别仅在于雇员的身份证号。我们可以通过身份证号来顺序存储每个雇员,但是在内存中实现呢?是不是要准备足够的内存来存储1000个雇员,然后再将这些雇员逐一插入?如果已经插入了500条记录,这时需要插入一个身份证号较低的新雇员,该怎么办呢?是在内存中将500条记录全部下移后,再从开头插入新的记录? 还是创建一个映射来记住每个对象的位置? 当决定如何存储对象的集合时,必须考虑如下问题。 对于对象集合,必须执行的操作主要以下三种: ◆添加新的对象 ◆删除对象 ◆查找对象 我们必须确定如何将新的对象添加到集合中。可以将对象添加到集合的末尾、开头或者中间的某个逻辑位置。 从集合中删除一个对象后,对象集合中现有对象会有什么影响呢?可能必须将内存移来移去,或者就在现有对象所驻留的内存位置下一个“洞”。 在内存中建立对象集合后,必须确定如何定位特定对象。可建立一种机制,利用该机制可根据某些搜索条件(例如身份证号)直接定位到目标对象;否则,便需要遍历集合中的每个对象,直到找到要查找的对象为止。 前面大家已经学习过了数组。数组的作用是可以存取一组数据。但是它却存在一些缺点,使得无法使用它来比较方便快捷的完成上述应用场景的要求。 1.首先,在很多数情况下面,我们需要能够存储一组数据的容 器,这一点虽然数组可以实现,但是如果我们需要存储的数据

Java类集合之List详解

Java集合框架之List详解 ArrayList 首先我们熟悉下ArrayList类中常用方法的使用。 1)添加:public boolean add(Object e):将指定的元素(对象)添加到此列表的尾部 2)获取:public Object get(int index):返回此列表中指定位置(索引)上的元素。 3)元素的个数:public int size():返回此列表中的元素数。 4)清空:public void clear():移除此列表中的所有元素。此调用返回后,列表将为空。 5)是否为空:public boolean isEmpty():如果此列表中没有元素,则返回 true 6)移除(删除):public E remove(int index):移除此列表中指定位置上的元素。向左移动所有后续元素(将其索引减 1)。 7)移除(重载):public boolean remove(Object o):移除此列表中首次出现的指定元素(如果存在)。如果列表不包含此元素,则列表不做改动。更确切地讲,移除满足 (o==null ? get(i)==null : o.equals(get(i))) 的最低索引的元素(如果存在此类元素)。如果列表中包含指定的元素,则返回 true (或者等同于这种情况:如果列表由于调用而发生更改,则返回 true)。8)获得索引:public int indexOf(Object o): 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。更确切地讲,返回满足(o==null ? get(i)==null : o.equals(get(i))) 的最低索引 i ,如果不存在此类索引,则返回 -1。 如何使用这些方法,代码如下: import java.util.ArrayList; public class ArrayListTest { public static void main(String[] args) { ArrayList list=new ArrayList(); /* * 添加 */ list.add("hello"); list.add(" world"); list. add(" welcome"); /* * 获得 */ String s1=(String)list.get(0);

相关文档
最新文档