【Java心得总结五】Java容器上——容器初探

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

而在我们计算机科学种集合的定义是:集合是一组可变数量的数据项(也可能是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在设计容器类库的时候为我们留下了空间,即我们可以向容器类库中装入任何我们需要的对象(当然了一个容器对象只能够装入一组相同继承源的对象)。比如


1 public class Container{
2 public static void main(String[] args){
3 List<Integer> li = new ArrayList<Integer>();
4 List<String> ls = new ArrayList<String>();
5 Map<Integer> mi = new HashMap<Integer>();
6 Map<String> ms = new HashMap<String>();
7 }
8 }

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

三、Foreach与迭代器

1)初识迭代器

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

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


 1 import java.util.*;
2 public class ForEachCollections {
3 public static void main(String[] args) {
4 Collection<String> cs = new LinkedList<String>();
5 Collections.addAll(cs,
6 "Take the long way home".split(" "));
7 for(String s : cs)
8 System.out.print("‘" + s + "‘ ");
9 }
10 } /* Output:
11 ‘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接口的类


 1 import java.util.*;
2 public class IterableClass implements Iterable<String> {
3 protected String[] words = ("And that is how " +
4 "we know the Earth to be banana-shaped.").split(" ");
5 public Iterator<String> iterator() {
6 return new Iterator<String>() {
7 private int index = 0;
8 public boolean hasNext() {
9 return index < words.length;
10 }
11 public String next() {
12 return words[index++];
13 }
14 public void remove() { // Not implemented
15 throw new UnsupportedOperationException();
16 }
17 };
18 }
19 public static void main(String[] args) {
20 for(String s : new IterableClass())
21 System.out.print(s + " ");
22 }
23 } /* Output:
24 And that is how we know the Earth to be banana-shaped.
25 *///:~

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

3)适配Collection的迭代器接口

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

利用适配器方法来实现:


 1 import java.util.*;
2 class ReversibleArrayList<T> extends ArrayList<T> {
3 public ReversibleArrayList(Collection<T> c) { super(c); }
4 public Iterable<T> reversed() {
5 return new Iterable<T>() {
6 public Iterator<T> iterator() {
7 return new Iterator<T>() {
8 int current = size() - 1;
9 public boolean hasNext() { return current > -1; }
10 public T next() { return get(current--); }
11 public void remove() { // Not implemented
12 throw new UnsupportedOperationException();
13 }
14 };
15 }
16 };
17 }
18 }
19 public class AdapterMethodIdiom {
20 public static void main(String[] args) {
21 ReversibleArrayList<String> ral =
22 new ReversibleArrayList<String>(
23 Arrays.asList("To be or not to be".split(" ")));
24 // Grabs the ordinary iterator via iterator():
25 for(String s : ral)
26 System.out.print(s + " ");
27 System.out.println();
28 // Hand it the Iterable of your choice
29 for(String s : ral.reversed())
30 System.out.print(s + " ");
31 }
32 } /* Output:
33 To be or not to be
34 be to not or be To
35 *///:~

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

总结:

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

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

时间: 2024-08-26 02:48:38

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

【Java心得总结五】Java容器中——Collection

在[Java心得总结五]Java容器上——容器初探这篇博文中,我对Java容器类库从一个整体的偏向于宏观的角度初步认识了Java容器类库.而在这篇博文中,我想着重对容器类库中的Collection容器做一个着重的探索与总结. Collection:一个独立元素的序列,这些元素都服从一条或多条规则.(注:Collection其实就是将一组数据对象按照一维线性的方式组织起来)List必须按照插入的顺序保存元素,而set不能有重复元素.Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序

别样JAVA学习(五)继承上

上章我们进行了面向对象的学习, 发现如果定义的几个类中的属性和方法重复, 代码是不是会显得很冗余啊?,有没有一种思想可以简化呢? 有!下面我们来看继承就能解决这个问题 1.继承-概述 继承: 1,提高了代码的复用性 2,让类与类之间产生了关系,也就是多态的特性 注意:千万不要为了获取其它类的功能,简化代码而继承 必须是类与类之间有所属关系才可以继承. Java语言中:java只支持单继承,不支持多继承 因为多继承容易带来安全隐患:当多个父类中定义了相同功能 且功能内容不同时,子类对象不确定运行哪

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

接下来说完equals以后,我们学习接下来的toString(), Java又觉得全部对象不光具有比較性, 还能使对象变成字符串被打印. 出现 曾经前面显示的是数组.如今显示的是这个对象所属的类. 紧跟着是这个对象的哈希值,也就是全部的对象都有哈希值(内存地址). 接下来.改动ObjectDemoToString class类 class ObjectDemoToString { public static void main(String[] args) { Demo d1=new Demo(

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

我将容器类库自己平时编程及看书的感受总结成了三篇博文,前两篇分别是:[Java心得总结五]Java容器上——容器初探和[Java心得总结六]Java容器中——Collection,第一篇从宏观整体的角度对Java中强大的容器类库做了一个简单总结而第二篇专门针对容器类库中的Collection部分进行了总结.这篇博文将对容器类库中的Map部分进行一个整理总结. 一.初识Map Map:一组成对的“键值对”对象,允许你使用键来查找值.(注:Map其实是将键与值形成的二元组按照一维线性的方式组织起来,

Java并发(5):同步容器

一. 同步容器出现的原因 在Java的集合容器框架中,主要有四大类别:List.Set.Queue.Map. List.Set.Queue接口分别继承了Collection接口,Map本身是一个接口. 注意Collection和Map是一个顶层接口,而List.Set.Queue则继承了Collection接口,分别代表数组.集合和队列这三大类容器. 像ArrayList.LinkedList都是实现了List接口,HashSet实现了Set接口,而Deque(双向队列,允许在队首.队尾进行入队

Java 并发编程(四)并发容器

并发容器 Java 5.0 提供了多种并发容器来改进同步容器的性能.         同步容器是将所有对容器的访问都串行化,以实现他们的线程安全性.代价是严重降低并发行,当多个线程竞争容器的锁时,吞吐量将严重降低.         并发容器是针对多个线程并发访问设计的. Java 5.0 增加了 ConcurrentHashMap ,用来替代同步且基于散列的 Map ,增加了 CopyOnWriteArrayList ,用于在遍历操作为主要操作的情况下替代同步的 List . Java 5.0

Scripting Java(二):使用Spring容器

前面我们已经知道了怎么在Java中执行脚本语言,今天,以Groovy为栗,来看下怎么在脚本里面使用Spring容器. Bindings 最简单的方式,直接将ApplicationContext丢到ScriptEngine的上下文环境,也就是Bindings里面,这样脚本里面就可以直接使用ApplicationContext.getBean来拿到容器里面的bean了. import java.util.Random; public class Foo { private int i; public

Java 容器:Collection 初探之 List

1 1 ///: JavaBasic//com.cnblogs.pattywgm.day1//CollectionTest.java 2 2 3 3 package com.cnblogs.pattywgm.day1; 4 4 5 5 import java.io.BufferedReader; 6 6 import java.io.IOException; 7 7 import java.io.InputStreamReader; 8 8 import java.util.ArrayList;

java的异常和java web容器的异常

一.java的异常,只要catch住异常了,程序就不会挂,依然会执行catch之后的语句 Java程序发生异常就挂了吗? 为了验证程序不会挂,我写了个例子给大家看看. 测试代码: import java.io.File; import java.io.IOException; /** * Java程序发生异常就挂了吗? * */ publicclass TestException {         publicstaticvoid main(String[] args) {