1. Java 集合框架概述
一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。
另一方面,使用Array存储对象方面具有一些弊端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。
数组在内存存储方面的特点:
- 数组初始化以后,长度就确定了。
- 数组声明的类型,就决定了进行元素初始化时的类型
- 数组存储的数据是有序的、可以重复的
- 存储数据的特点单一
数组在存储数据方面的弊端:
- 数组初始化以后,长度就不可变了,不便于扩展
- 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。
- 无法直接获取存储元素的个数
Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的
2. 集合的使用场景
- 将JSON对象或JSON数组转换为Java对象或Java对象构成的List
- 将Java对象或Java对象构成的List转换为JSON对象或JSON数组
3. Collection和Map接口继承树
Collection接口:单列数据,定义了存取一组对象的方法的集合
List:元素有序、可重复的集合
Set:元素无序、不可重复的集合
Map接口:双列数据,保存具有映射关系“key-value对”的集合


4. Collection 接口方法
- 添加
.add(Object obj)
.addAll(Collection coll)
- 获取有效元素的个数 int
.size()
- 清空集合 void
.clear()
- 是否是空集合 boolean
.isEmpty()
- 是否包含某个元素 boolean
.contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
.containsAll(Collection c):也是调用元素的equals方法来比
- 删除 boolean
.remove(Object obj) :通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
.removeAll(Collection coll):取当前集合的差集
- 取两个集合的交集 boolean
.retainAll(Collection c):把交集的结果存在当前集合中,不影响c
- 集合是否相等 boolean
.equals(Object obj)
- 转成对象数组 Object[]
.toArray()
- 获取集合对象的哈希值
.hashCode()
- 遍历
.iterator():返回迭代器对象,用于集合遍历
5. Iterator 接口
- Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
- GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公交车上的售票员”、“火车上的乘务员”、“空姐”。
- Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
- Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建Iterator 对象,则必须有一个被迭代的集合。
- 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
Iterator接口的方法
.hasNext()
.next( )
.remove( )
在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常


Iterator接口remove()方法
Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方法,不是集合对象的remove方法。
如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
6. foreach 循环
- Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
- 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
- 遍历集合的底层调用Iterator完成操作。
- foreach还可以用来遍历数组
7. List接口
- 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
- List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
List接口方法
List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法
.add(int index, Object ele):在index位置插入ele元素 boolean
.addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来 Object
.get(int index): 获取指定index位置的元素 int
.indexOf(Object obj):返回obj在集合中首次出现的位置 int
.lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 Object
.remove(int index):移除指定index位置的元素,并返回此元素 Object
.set(int index, Object ele):设置指定index位置的元素为ele List
.subList(int fromIndex, int toIndex):返回从fromIndex到toIndex
8. ArrayList
ArrayList 是 List 接口的典型实现类、主要实现类
本质上,ArrayList是对象引用的一个”变长”数组
ArrayList的JDK1.8之前与之后的实现区别?
JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
ArrayList源码分析
//数组属性
private transient Object[] elementData;
//无参构造
public ArrayList() {
this(10);
}
//初始化数组长度
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//元素数量大于数组长度时
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//正常情况下扩展1.5倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //10
int newCapacity = oldCapacity + (oldCapacity >> 1);//右位移相当于除以2
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
//jdk 8中ArrayList的变化:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
后续的添加和扩容操作与jdk 7 无异。
9. LinkedList
对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
新增方法:
.addFirst(Object obj)
.addLast(Object obj)
.getFirst()
.getLast()
.removeFirst()
.removeLast()
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
prev变量记录前一个元素的位置
next变量记录下一个元素的位置

LinkedList源码解析
//基本属性
transient int size = 0;
transient Node<E> first;//集合第一个元素
transient Node<E> last;//集合最后一个元素
//Node属性
E item; //当前节点
Node<E> next; //下一个节点
Node<E> prev; //上一个节点
//添加元素方法
public boolean add(E e) {
linkLast(e);
return true;
}
//如果是第一次添加last为null,l=null,把新节点赋给last和first属性
//如果第二次添加,last不为空 l不为空,新节点把l作为自己的first,新节点赋给last,新节点赋给l的next属性
void linkLast(E e) {
final Node<E> l = last; //last=1号元素 l=1号元素
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
//修改次数
modCount++;
}
10. Vector
Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
新增方法:
.addElement(Object obj)
.insertElementAt(Object obj,int index)
.setElementAt(Object obj,int index)
.removeElement(Object obj)
.removeAllElements()
ArrayList和LinkedList的异同
二者都线程不安全,执行效率高,线程安全的Vector。此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
11. Set 接口
Set接口是Collection的子接口,set接口没有提供额外的方法
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
Set 无序性不等于随机性,不是指遍历时的输出顺序,无序性指的是在存储元素时位置的不确定性
12. HashSet
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 是数组和链表的结合体
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
HashSet 具有以下特点:
不能保证元素的排列顺序
HashSet 不是线程安全的
集合元素可以是 null
HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)
HashSet中添加元素的过程
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。hashCode() 方法可以简单的理解成一个随机函数(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)
如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置 , 但依然可以添加成功

重写 hashCode()和equals()方法的基本原则
相等的对象必须具有相等的散列码
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。
对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
Eclipse/IDEA工具里hashCode()的重写
@Override
public int hashCode() {
//31是放大系数,系数越大,通过计算出现重复的几率就越小
//也不能太大,太大容易数据溢出
//要是个素数
//采用2的幂-1的方案,例如:2的1次方-1 是1 2的2次方-1是3 2的三次方-1是7 2的四次方-1是15 2的5次方-1 是31 2的6次方-1是63,其中3 7 31是素数,3和7太小,因此用31做放大系数
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
13. LinkedHashSet
LinkedHashSet 是 HashSet 的子类
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
LinkedHashSet 不允许集合元素重复(原理同HashSet)

14. TreeSet
TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
不能添加不同类的对象
没有重写Comparator方法不能添加对象
TreeSet底层使用红黑树结构存储数据
新增的方法如下: (了解)
.Comparator comparator()
.Object first()
.Object last()
.Object lower(Object e)
.Object higher(Object e)
.SortedSet subSet(fromElement, toElement)
.SortedSet headSet(toElement)
.SortedSet tailSet(fromElement)
lTreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet
15. 红黑树
小数存左边,大数存右边

参考资料:数据结构概述-Java版
16. 自然排序
TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable接口。
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。
Comparable 的典型实现:
BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较
Character:按字符的 unicode值来进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
String:按字符串中字符的 unicode 值进行比较
Date、Time
向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象。
对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值。
当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回 0。否则,让人难以理解
17. 定制排序
TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来实现。需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。
此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0
18. Map接口继承树

19. Map接口
Map与Collection并列存在。用于保存具有映射关系的数据:key-value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
常用String类作为Map的“键”
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是 Map 接口使用频率最高的实现类

20. jdk7中HashMap的存储原理
- 在实例化以后,底层创建了长度是16的一维数组Entry[] table
- 执行map.put(key1,value1)后
- 调用key1所在类的hashCode()计算key1哈希值
- key1哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
- 如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
- 如果此位置上的数据不为空,如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
- 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,equals()返回false:此时key1-value1添加成功。----情况3
- 如果equals()返回true:使用value1替换value2。--情况4
- 关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。

21. jdk8中HashMap的存储原理
- new HashMap():底层没有创建一个长度为16的数组
- jdk 8底层的数组是:Node[],而非Entry[]
- 首次调用put()方法时,底层创建长度为16的数组
- jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树
- 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储

22. jdk8中hashMap源码解析


final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//jdk8中hashmap底层是Node数组不再是entry数组
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果table数组为空,初始化数组
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//通过hash值计算位置,并且位置上没有元素,添加元素
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//如果hash值相等并==或equals为true,把元素赋值给e,最终实现替换
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
//如果元素本身是一个TreeNode,调用putTreeVal添加元素
e = ((TreeNode<K,V>)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;
}
//如果存在相同的则退出循环,实现替换
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
//替换元素
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
//元素数量加1
++modCount;
//元素数量超过阈值,扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
/**
* 对链表进行红黑树的处理
*/
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
//如果数组为null或者数组长度小于64进行扩容,不进行红黑树处理
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
//使用循环处理红黑树
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
23. 常用方法
添加、删除、修改操作:
.put(Object key,Object value):将指定key-value添加到(或修改)当前map对象 Object
.putAll(Map m):将m中的所有key-value对存放到当前map中 void
.remove(Object key):移除指定key的key-value对,并返回value Object
.clear():清空当前map中的所有数据 void
元素查询的操作:
.get(Object key):获取指定key对应的value Object
.containsKey(Object key):是否包含指定的key boolean
.containsValue(Object value):是否包含指定的value boolean
.size():返回map中key-value对的个数 int
.isEmpty():判断当前map是否为空 boolean
.equals(Object obj):判断当前map和参数对象obj是否相等 boolean
元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set
关于映射关系的key是否可以修改?
不要修改 映射关系存储到HashMap中会存储key的hash值,这样就不用在每次查找时重新计算每一个Entry或Node(TreeNode)的hash值了,因此如果已经put到Map中的映射关系,再修改key的属性,而这个属性又参与hashcode值的计算,那么会导致匹配不上
负载因子值的大小,对HashMap有什么影响
负载因子的大小决定了HashMap的数据密度。
负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长, 造成查询或插入时的比较次数增多,性能会下降。
负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数
24. LinkedHashMap
LinkedHashMap 是 HashMap 的子类
在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
源码解析:
- put方法调用HashMap的put方法,以及putVal方法
- 重写了newNode方法

- LinkedHashMap.Entry继承了HashMap.Node

25. TreeMap
TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。
TreeSet底层使用红黑树结构存储数据
TreeMap 的 Key 的排序:
自然排序:
TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:
创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现Comparable 接口
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0
26. Properties
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key和 value 都是字符串类型
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
27. Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类
Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
排序操作:(均为static方法)
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j
查找、替换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List
Collections常用方法:同步控制


Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
Collection和Collections的区别
collection是接口,Collections是操作集合的工具类
Enumeration
lEnumeration 接口是 Iterator 迭代器的 “古老版本”
Enumeration stringEnum = new StringTokenizer("a-b*c-d-e-g", "-");
while(stringEnum.hasMoreElements()){
Object obj = stringEnum.nextElement();
System.out.println(obj);
}
Comments | 1 条评论
Can I just say what a comfort to find somebody who truly understands what theyre talking about on the net. You certainly understand how to bring a problem to light and make it important. A lot more people really need to look at this and understand this side of the story. I cant believe youre not more popular because you definitely possess the gift.