在数学中我们有集合的概念,所谓的一个集合,就是将数个对象归类而分成为一个或数个形态各异的大小整体。 一般来讲,集合是具有某种特性的事物的整体,或是一些确认对象的汇集。构成集合的事物或对象称作元素或是成员。集合具有:无序性、互异性、确定性。

而在我们计算机科学种集合的定义是:集合是一组可变数量的数据项(也可能是0个)的组合,这些数据项可能共享某些特征,需要以某种操作方式一起进行操作。一般来讲,这些数据项的类型是相同的,或基类相同(若使用的语言支持继承)。列表(或数组)通常不被认为是集合,因为其大小固定,但事实上它常常在实现中作为某些形式的集合使用。

——参考自维基百科

通过上面的描述,我们很清楚的发现Java中容器存在的意义。在平常的生活中我们也会经常用到集合的概念来处理问题,自然而然我们用编程解决实际生活当中的问题也就要将集合用到我们的编程中。

在Java编程中,当我们想要持有一组类型相同或者基类相同的对象,我们不可能去分别持有每个对象的引用,那将是一件恐怖的事情。我们的解决办法就是将这组对象放在一个集合当中,也许有的人会说,那我们直接使用数组就可以了啊,在上面描述中我们也提到了数组的局限性,就是它的大小固定,而我们的应用经常需要的是动态的扩展集合大小。(当然一方面Java容器类库中的底层也用到了数组加以实现,另一方面数组也有其高效便捷的特性)而Java为我们做了更进一步的封装,容器类库使我们更加可以使用集合或说面向对象的观念去解决我们实际碰到的问题,java中的容器类库基本可以说是我们平时编程使用最频繁的类库。

下面我们首先从宏观整体的角度观摩下Java中容器类库的层次结构:

一、容器类的层次结构

简单的容器分类

——摘自 《Thinking in java》第17章图

(图的解释:点线框表示接口,实线框表示普通的(具体的)类。带有空心箭头的点线表示一个特定的类实现了一个接口,实心箭头表示某个类可以生成箭头所指向类的对象)

从图中我们可以很清晰的发现容器类库从基类角度分析可以分为两个模块:Collection、Map

1)Collection:一个独立元素的序列,这些元素都服从一条或多条规则。(注:Collection其实就是将一组数据对象按照一维线性的方式组织起来)List必须按照插入的顺序保存元素,而set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。

2)Map:一组成对的“键值对”对象,允许你使用键来查找值。(注:Map其实是将键与值形成的二元组按照一维线性的方式组织起来,这里值得注意的是值可以使一个Collection或者Map,即嵌套结构,如:Map<Integer,List<String>>,Map<String,Map<String>>)从另一个角度来考虑Map,其实Map相当于ArrayList或者更简单的数组的一种扩展、推广。在数组中我们可以利用下标即数字访问数组当中的不同元素,那么数字与对象之间形成了一种关联,那么如果将这个数字的概念扩展成为对象,那同样的我们可以将对象与对象之间关联起来。即Map,也称为映射表、关联数组、字典允许我们使用一个对象来查找某个对象。

二、容器与泛型

我们有了容器库,相当于有了木桶,下面要往里面放入对象,可是这个对象并不是确定的,只有针对具体的编程环境才会有具体的应用,就像木桶做出来你并不知道它用来盛放什么(可以是水,沙子,油等等)。这就不得不说一个和容器类库紧密相关的概念泛型(泛型可参见【Java心得总结三】Java泛型上——初识泛型【Java心得总结四】Java泛型下——万恶的擦除

简单的说,Java在设计容器类库的时候为我们留下了空间,即我们可以向容器类库中装入任何我们需要的对象(当然了一个容器对象只能够装入一组相同继承源的对象)。比如

 public class Container{
public static void main(String[] args){
List<Integer> li = new ArrayList<Integer>();
List<String> ls = new ArrayList<String>();
Map<Integer> mi = new HashMap<Integer>();
Map<String> ms = new HashMap<String>();
}
}

上面我分别用Collection中的List和Map做了示例,很明显我们可以看出利用泛型我们可以向容器中放入任何我们需要的类型,甚至可以使自己创建的类(当然有时需要我们实现一些接口,具体我将在后续博文中进行总结)

三、Foreach与迭代器

1)初识迭代器

上面那张图中我们简单讨论了Collection和Map,还有一个部分就是Iterator,即迭代器。

相信熟悉面向对象编程的朋友对foreach一定不会陌生,foreach语句可以更好地支持我们对于集合的遍历操作。

 import java.util.*;
public class ForEachCollections {
public static void main(String[] args) {
Collection<String> cs = new LinkedList<String>();
Collections.addAll(cs,
"Take the long way home".split(" "));
for(String s : cs)
System.out.print("‘" + s + "‘ ");
}
} /* Output:
‘Take’ ‘the’ ‘long’ ‘way’ ‘home’

上面的代码cs是一个Collection,这也说明了foreach能够与所有Cellection对象一起工作(包括List,Set,Queue),另外需要注意的是Java中对foreach的操作关键字仍然是for而不像C#中关键字直接变为了foreach。

这里我们会有一个疑问,foreach是怎么做到在集合中移动从而达到遍历的效果呢?(因为像如果我们用普通的for循环如:for(int i = 0; i < 10; i++){},其中i就是我们一直在移动的变量,从而达到了遍历数组的效果。)

答案是Iterable接口,该接口包含一个能够产生Iterator的iterator()方法,foreach就是使用Iterator在序列中移动的。因此如果你创建了任何实现了Iterable的类,都可以将它用于foreach语句中(着我们就看出了为什么Iterator接口会出现在上图中了,因为所有的Collection类都实现了Iterator接口,它们都可以产生迭代器供foreach使用,特别注意Map并没有实现Iterator接口我们需要利用别的方式来遍历Map,后续)

2)创建实现Iterable接口的类

 import java.util.*;
public class IterableClass implements Iterable<String> {
protected String[] words = ("And that is how " +
"we know the Earth to be banana-shaped.").split(" ");
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
public boolean hasNext() {
return index < words.length;
}
public String next() {
return words[index++];
}
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args) {
for(String s : new IterableClass())
System.out.print(s + " ");
}
} /* Output:
And that is how we know the Earth to be banana-shaped.
*///:~

上面的例子我们在自己创建的类IterableClass中实现了Iterator<String>接口,并且通过Iterator()方法返回的是实现了Iterator<String>的匿名内部类的实例,该匿名内部类可以遍历数组中的所有单词。这样在main中我们将IterableClass应用于了foreach语句。(关于匿名内部类,博文后续)

3)适配Collection的迭代器接口

针对我们自己创建的类我们可以通过实现Iterable接口来完成我们自己需要的遍历操作。然而如果我们利用foreach操作遍历Java类库中提供的Collection时,就会限制我们的遍历操作,因为如Java在ArrayList中只提供了从前往后的顺序遍历迭代器,假设我想要逆序遍历,那怎么办呢?

利用适配器方法来实现:

 import java.util.*;
class ReversibleArrayList<T> extends ArrayList<T> {
public ReversibleArrayList(Collection<T> c) { super(c); }
public Iterable<T> reversed() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = size() - 1;
public boolean hasNext() { return current > -1; }
public T next() { return get(current--); }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
};
}
}
public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList<String> ral =
new ReversibleArrayList<String>(
Arrays.asList("To be or not to be".split(" ")));
// Grabs the ordinary iterator via iterator():
for(String s : ral)
System.out.print(s + " ");
System.out.println();
// Hand it the Iterable of your choice
for(String s : ral.reversed())
System.out.print(s + " ");
}
} /* Output:
To be or not to be
be to not or be To
*///:~

在这段代码中我们声明了ReversibleArrayList<T>类用来做我们的适配器(适配器设计模式),这里我们继承自ArrayList<T>类(Collection类库已有类),并且在此基础上扩展了一个reversed()方法,它会返回一个逆序遍历数组的迭代器,从而达到我们的目的。在main函数中我们看到了在代码第29行我们调用了我们扩展的reversed()方法返回了不同的迭代器,达到了逆序遍历的效果。

总结:

这篇博文从宏观整体的角度把握了java类库为我们提供的容器类库,并且也详细阐明了在实际中对迭代器的使用方式。在后续两篇博文中,我会分别整理Collection和Map(请参见博文【Java心得总结六】Java容器中——Collection【Java心得总结七】Java容器下——Map

参考——《Java编程思想第4版》

【Java心得总结五】Java容器上——容器初探的更多相关文章

  1. 和朱晔一起复习Java并发(五):并发容器和同步器

    本节我们先会来复习一下java.util.concurrent下面的一些并发容器,然后再会来简单看一下各种同步器. ConcurrentHashMap和ConcurrentSkipListMap的性能 ...

  2. 别样JAVA学习(五)继承上(1.0)Object类equals()

    上一节继承下(一)我们进行抽象类.接口以及多态的学习. 接下来大家我们讲点特殊的东西就是object类, 我们一直在说继承,子继承了父,父还有没有父类呢, 为什么这么思考,大家想构造函数的第一行是不是 ...

  3. 别样JAVA学习(五)继承上(1.1)Object类toString()

    接下来说完equals以后,我们学习接下来的toString(), Java又觉得全部对象不光具有比較性, 还能使对象变成字符串被打印. 出现 曾经前面显示的是数组.如今显示的是这个对象所属的类. 紧 ...

  4. Java进阶(十五)Java中设置session的详细解释

    Java中设置session的详细解释 简单通俗的讲session就是象一个临时的容器,用来存放临时的东西.从你登陆开始就保存在session里,当然你可以自己设置它的有效时间和页面,举个简单的例子: ...

  5. java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的

    本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...

  6. Java学习笔记五:Java中常用的运算符

    Java中常用的运算符 运算符是一种“功能”符号,用以通知 Java 进行相关的运算.譬如,我们需要将变量 score 的值设置为 20 ,这时候就需要一个“=”,告诉程序需要进行赋值操作. Java ...

  7. 菜鸡的Java笔记 第五 - java 程序逻辑控制

    程序主要分为三种逻辑:顺序,分支,循环. if 分支语句 if分支语句是最为基础的分支操作,但是其有三种使用形式: if语句 if.....else   语句 if....else...if...el ...

  8. Java 学习笔记 (五) Java Compile\Build\Make的区别

    以下内容引自: http://blog.51cto.com/lavasoft/436216 Compile.Make和Build的区别 原创leizhimin2010-11-30 11:30:20评论 ...

  9. 【Java心得总结七】Java容器下——Map

    我将容器类库自己平时编程及看书的感受总结成了三篇博文,前两篇分别是:[Java心得总结五]Java容器上——容器初探和[Java心得总结六]Java容器中——Collection,第一篇从宏观整体的角 ...

随机推荐

  1. 【luogu】 P1880 石子合并

    原题原题原题原题原题 先贴上错误代码... ↓错误代码↓ #include <iostream> #include <cstdio> #include <cstring& ...

  2. 20145337 GDB调试汇编堆栈过程分析

    20145337 GDB调试汇编堆栈过程分析 测试代码 #include<stdio.h> short addend1 = 1; static int addend2 = 2; const ...

  3. css确定元素水平居中和垂直居中

    ---恢复内容开始--- 首先,我们在了解如何通过css了解元素水平和垂直居中之前,先要了解下html都有哪些元素,这些元素与偶有哪些分类,因为不同类别的元素的水平垂直居中方法是完全不同的,究其根本当 ...

  4. 一个基于Orchard的开源CRM --coevery简介

    Coevery是开源的.NET Web平台项目,力争打造一个开放而鲁棒的CRM系统,采用Orchard架构,并使用AngularJS改善页面体验.作为一个后发优势的CRM 产品,Coevery 具有一 ...

  5. (新年快乐)ABP理论学习之本地化(2016第一篇)

    返回总目录 本篇目录 应用语言 本地化资源 获取本地化文本 扩展本地化资源 最佳实践 应用语言 一个应用至少有一种UI语言,许多应用不止有一种语言.ABP为应用提供了一个灵活的本地化系统. 第一件事情 ...

  6. Python黑帽编程 2.0 第二章概述

    Python黑帽编程 2.0 第二章概述 于 20世纪80年代末,Guido van Rossum发明了Python,初衷据说是为了打发圣诞节的无趣,1991年首次发布,是ABC语言的继承,同时也是一 ...

  7. 搞懂 SynchronizationContext(第一部分)【翻译】

    SynchronizationContext -MSDN 很让人失望 我不知道为什么,目前在.Net下关于这个类只有很少的资料.MSDN文档也只有很少的关于如何使用SynchronizationCon ...

  8. 蓄水池(Reservoir_sampling)抽样算法简记

    摘要 1.适用场合 2.算法简介 3.代码例子 4.Spark RangePartitioner 中的应用(待补充) 内容 1.适用场合:从包含n个项目的集合S中选取k个样本,其中n为一很大或未知的数 ...

  9. 【PRINCE2是什么】PRINCE2认证之七大原则(4)

    我们先来回顾一下,PRINCE2七大原则分别是持续的业务验证,经验学习,角色与责任,按阶段管理,例外管理,关注产品,剪裁. 第四个原则:按阶段管理. 阶段管理其实是给高层提供了项目生命周期中相对应的控 ...

  10. Bootstrap~学习笔记索引

    回到占占推荐博客索引 bootstrap已经用了有段时间了,感觉在使用上还是比较容易接受的,在开发人员用起来上,也还好,不用考虑它的兼容性,手机,平台,PC都可以有效的兼容. bootstrap官方a ...