一、前言

容器是JAVA中比较重要的一块,整个体系设计得非常好,同时对于代码学习来说也是比较好的范例。同时很多面试官也比较喜欢用容器来考察面试者的基础知识,所以掌握好容器还是比较重要的。本文主要总结一下所有容器的公共接口之一Collection以其抽象实现AbstractCollection.

二、Collection介绍

JDK的官方文档对Collection的定义是这样的:The root interface in the collection hierarchy. A collection represents a group of objects, known as its elements. Some collections allow duplicate elements and others do not. Some are ordered and others unordered.

  通过定义我们知道Collection表示一组对象,根据集合类型的不同,有的允许重复元素,有的是有序的,这个要看具体的子接口的实现情况。Collection接口中定义一些通用的方法。这些方法都比较基本而且使用都比较频繁,所以我们需要对每一个方法都记录,按方法的作用我们可以分为以下几类:

1. 添加

共两个方法,分别是add和addAll, 分别是接收一个对象和一个Collection对象。

2. 删除

共四个方法,remove, removeAll, retainAll,clear, 其中需要说的是retainAll,这个操作接受一个Collection作为参数,取两个集合的交集。

3. 查找

共两个方法, contains, containsAll, 判断集合中是否有某个或某些元素

4. 转换

共三个方法, toArray(), toArray(T t[])和iterator, 前两个是把集合转化为数组,另外一个是转化为一个Iterator对象,可用于遍历,这个方法其实在其父接口Iterable中也有定义。

5. 求大小

共两个方法, size()和isEmpty(), 分别是求长度和判断集合是否为空。

6. 比较

共两个方法,equals和hashCode,这两个方法是从object中继承过来的。

以上共15个方法,大多数还是很好理解。需要重点关注的是转换类的两个方法,由于Collection继承了Iterable,所以所有的collection都可以通过foreach的方式来调用,这是JDK1.5之后的一种语法糖。

关于Collection的介绍就到这里,下面接着看一下其直接骨架类AbstractCollection.

三、AbstractCollection介绍

虽然Collection中的方法很多,其不同子类型的表现也不一样,但事实上这15个方法中有很多都是跟具体的子类没有关系的,为了简化具体Collection类的设计, JDK提供了一个抽象类AbstractCollection,对其中的大多数方法进行了实现。

方法的实现没有必要依次去介绍,这里主要介绍这个子类的一些特点及几个重要方法的实现。

1. 本类默认是不是可修改的,即不支持add,由于addAll依赖于add,所以addAll也是不支持的,要支持添加功能,就需要重写这个add方法

2. size,iterator这两个方法没有实现,所以要编写自己的collection,需要实现这两个方法即可。

3. 其它所有方法都有实现,不过涉及到遍历的都依赖于iterator()返回的迭代器,删除也依赖于迭代器提供的删除方法。

4. 本类没有对equals和hashCode进行重写,但是对toString进行了重写。

大部分方法的实现很容易理解,下面重点介绍一下toArray。

四、重点方法分析

Collection可以直接转化为数组,本接口中有两个方法,Object[] toArray()和<T> T[] toArray(T[] a),相信有些人和我一样,在使用时会感觉到困惑,一是不知道使用哪个方法,二是不知道第二个方法的参数和返回值之间有什么关系,下面我们就来认真分析一下。

先看一下JDK对于这个方法的定义描述:The returned array will be "safe" in that no references to it are maintained by this collection. (In other words, this method must allocate a new array even if this collection is backed by an array). The caller is thus free to modify the returned array.

从这个描述我们可以知道toArray得到的数组跟原collection没有任何关系,我们可以对数组的每个引用值做修改,而不会影响到原collection.这个看起来好像是多余说明的,但是考虑到ArrayList其实就是基于数组实现的,那这个限制保证了即使是将ArrayList转化为数组,那也应该是分配一个新数组,而不是返回原来的数组。

好了,我们再看一下具体的代码。

     public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
} private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
int i = r.length;
while (it.hasNext()) {
int cap = r.length;
if (i == cap) {
int newCap = cap + (cap >> 1) + 1;
// overflow-conscious code
if (newCap - MAX_ARRAY_SIZE > 0)
newCap = hugeCapacity(cap + 1);
r = Arrays.copyOf(r, newCap);
}
r[i++] = (T)it.next();
}
// trim if overallocated
return (i == r.length) ? r : Arrays.copyOf(r, i);
} private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError
("Required array size too large");
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

上面是AbstractCollection的实现,可以看到对于toArray()来说,就是分配了一个等大空间的数组,然后依次对数组元素进行赋值。

如果我们在单线程操作的情况下,collection集合大小不变,正常应该是执行到 return it.hasNext() ? finishToArray(r, it) : r; 这条语句结束,但考虑到在复制的过程中,collection的集合可能会有变化,可能是变大也可能是变小,所以方法增加了对这种情况的处理,这就是为什么每次循环都要判断是collection是否遍历完,以及最后再判断collection是否变得更长,如果是的话,还需要重新再为array分配空间。

通常情况下,我们不会执行到hugeCapacity,但作为一个框架来说,这体现了设计时的严谨。

可以看到,toArray返回的是一个Object数组,不能很好的体现collection中的元素类型,这样collection的泛型就无法体现出优势。所以,我们又有了第二个方法。个人当时在使用这个方法是,最大的疑惑就在于不知道这个参数应该怎么传,下面我们来看下具体的实现。

 public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator(); for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}

我们可以看到,方法在处理里,会先判断参数数组的大小,如果空间足够就使用参数作为元素存储,如果不够则新分配一个。在循环中的判断也是一样,如果参数a能够存储则返回a,如果不能再新分配。在看了这个之后,对于这新代码strList.toArray(new String[0])相信就很容易理解了。

在看了这两个方法后,我相信对于toArray的区别和使用就比较容易掌握了,个人建议还是使用第二种比较好一些,在参数的选择上,要么传递一个0长度的数组,要么就传递一个与集合等长的数组,但考虑到集合的可变性,我们应该使用这个方法的返回值,而不是直接使用参数数组。

五、总结

总的来说,Collection和AbstractCollection还是比较简单,但只有掌握了这两个简单的类,在学习后续的各种list和set的时候,我们才能更好的理解。

容器--Collection和AbstractCollection的更多相关文章

  1. java容器-Collection

    1.介绍    collection<E>是java中容器的最主要的接口,该接口继承于Iterable<E>,使得java中所有实现Collection<E>的容器 ...

  2. Java容器解析系列(2) 具体化的第一步——Collection到AbstractCollection

    在通向具体化的List,Queue之前,我们需要先了解一下Collection接口和AbstractCollection抽象类,这两个都是处于Collection顶层的存在. Collection接口 ...

  3. Java容器---Collection接口中的共有方法

    1.Collection 接口 (1)Collection的超级接口是Iterable (2)Collection常用的子对象有:Map.List.Set.Queue. 右图中实现黑框的ArrayLi ...

  4. java容器——Collection接口

    Collection是Set,List接口的父类接口,用于存储集合类型的数据. 2.方法 int size():返回集合的长度 void clear():清除集合里的所有元素,将集合长度变为0 Ite ...

  5. java容器collection的一些简单特点

    1.List ArrayList 可随机访问元素,但中间插入和一处元素较慢 LinkedList 在中间进行的插入和删除操作代价较小,随机访问比ArrayList较慢 特性集比ArrayList大 2 ...

  6. java中Collection容器

    1.容器(Collection)也称为集合, 在java中就是指对象的集合. 容器里存放的都只能是对象. 实际上是存放对象的指针(头部地址): 这里对于八种基本数据类型,在集合中实际存的是对应的包装类 ...

  7. JDK 高性能编程之容器

    高性能编程在对不同场景下对于容器的选择有着非常苛刻的条件,这里记录下前人总结的经验,并对源码进行调试 JDK高性能编程之容器 读书笔记内容部分来源书籍深入理解JVM.互联网等 先放一个类图util,点 ...

  8. Collection List Set和Map用法与区别

    labels:Collection List Set和Map用法与区别 java 散列表 集合 Collection           接 口的接口      对 象的集合   ├   List   ...

  9. Java 集合系列02之 Collection架构

    概要 首先,我们对Collection进行说明.下面先看看Collection的一些框架类的关系图: Collection是一个接口,它主要的两个分支是:List 和 Set. List和Set都是接 ...

随机推荐

  1. Xcode8新特性和iOS10新特性

    从 Xcode 8.0 开始,目前所有的插件都无法工作! NSLog 无法输出 -- 此bug等待正式版本... Xcode 提供了文档注释快捷键option + cmd + / 但是要把系统升级到1 ...

  2. Hadoop 2.4.1 Map/Reduce小结【原创】

    看了下MapReduce的例子.再看了下Mapper和Reducer源码,理清了参数的意义,就o了. public class Mapper<KEYIN, VALUEIN, KEYOUT, VA ...

  3. EvreryDay Collect

    1.在使用WebService时我们经常会考虑以下问题:怎么防止别人访问我的WebService? 在System.Net中提供了一个NetworkCredential,只有获得该凭证的用户才能访问相 ...

  4. Oracle数据库入门——如何根据物化视图日志快速刷新物化视图

    Oracle物化视图的快速刷新机制是通过物化视图日志完成的.Oracle如何通过一个物化视图日志就可以支持多个物化视图的快速刷新呢,本文简单的描述一下刷新的原理. 首先,看一下物化视图的结构:SQL& ...

  5. python导入自定义模块

    上网查了下资料和自己实验了下,有几个方法: 1.如果导入的模块和主程序在同个目录下,直接import就行了 2.如果导入的模块是在主程序所在目录的子目录下,可以在子目录中增加一个空白的__init__ ...

  6. tengine + mysql + nginx + php

    tengine + mysql + nginx + php 1.配置防火墙vim /etc/sysconfig/iptables # 允许80端口通过防火墙-A INPUT -m state --st ...

  7. tengine-2.1.0 + lua + base64

    参考:http://my.oschina.net/eduosi/blog/169606 安装 readline,lua 编译需要用到这个,centos 可以通过 yum 直接安装,如果不需要系统的,可 ...

  8. 基于SWFUpload的angular上传组件

    回顾 由于工作内容比较多,特别是架构方面,需要耗费很多的时间调整.重构,因此很久没有写文章了. 话就不多说了,直接进入主题. 实现 首先分析一下SWFUpload初始化的时候,需要传入当前触发上传的元 ...

  9. 审核流(3)低调奢华,简单不凡,实例演示-SNF.WorkFlow--SNF快速开发平台3.1

    下面我们就从什么都没有,结合审核流进行演示实例.从无到有如何快速完美的实现,然而如此简单.低调而奢华,简单而不凡. 从只有数据表通过SNF.CodeGenerator代码生成器快速生成单据并与审核流进 ...

  10. 二叉平衡查找树AvlTree(C实现)

    二叉平衡查找树即是一棵树中所有节点的左右子树高度差不超过1的查找树 头文件—————————————————————————————— #ifndef _AVLTREE_H_ #define _AVL ...