如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序。

11.1 泛型和类型安全的容器

使用ArrayList:创建一个实例,用add()插入对象,然后用get()访问对象,此时需要使用索引,像数组一样,但不是要[]。

import java.util.ArrayList;
public class ApplesAndOrangesWithoutGenerice
{
@SuppressWarnings("unchecked")//不受检查异常的警告
public static void main(String[] args)
{
ArrayList apples = new ArrayList();
for (int i = 0; i < 3; i++)
{
apples.add(new Apple());
}
apples.add(new Orange());
for (Object obj : apples)
{
System.out.println(((Apple) obj).id());
}
}
}
class Apple
{
private static long counter;
private final long id = counter++; public long id()
{
return id;
}
}
class Orange
{
}

ArrayList保存的是Object。

要想定义保存特定类型的ArrayLIst可以使用泛型<>。

import java.util.ArrayList;

public class ApplesAndOrangesWithoutGenerice
{
@SuppressWarnings("unchecked")//不受检查异常的警告
public static void main(String[] args)
{
ArrayList<Apple> apples = new ArrayList();
for (int i = 0; i < 3; i++)
{
apples.add(new Apple());
}
//apples.add(new Orange());
for (Object obj : apples)
{
System.out.println(((Apple) obj).id());
}
}
} class Apple
{
private static long counter;
private final long id = counter++; public long id()
{
return id;
}
} class Orange
{ }

当指定了某个类型作为泛型参数时,并不仅限于只能将该确切类型的对象放置到容器中,向上转型也可以像作用于其他类型一样作用于泛型。

import java.util.ArrayList;
public class ApplesAndOrangesWithoutGenerice
{
@SuppressWarnings("unchecked")//不受检查异常的警告
public static void main(String[] args)
{
ArrayList<Apple> apples = new ArrayList();
for (int i = 0; i < 3; i++)
{
apples.add(new A());
apples.add(new B());
apples.add(new C());
}
//apples.add(new Orange());
for (Object obj : apples)
{
System.out.println(((Apple) obj).id());
}
}
}
class Apple
{
private static long counter;
private final long id = counter++;
public long id()
{
return id;
}
}
class A extends Apple
{
}
class B extends Apple
{
}
class C extends Apple
{
}
class Orange
{ }

11.2 基本概念

Java容器类类库的用途是保存对象,并将其划分为两个不同的概念:

  • Collection。一个独立元素序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而set不能保存重复元素。
  • Map。一组成对的键值对对象,允许你使用键来查找值。映射表允许我们使用另一个对象来查找某个对象,它也被称为关联数组,因为它将对象于另外一个对象关联到了一起。或者被称为字典
List<Type> types=new ArrayList<Type>()

ArrayList被向上转型为List。

Conllection接口概括了序列的概念——一种存放一组对象的方式。

import java.util.ArrayList;
import java.util.Collection; public class SimpleCollection
{
public static void main(String[] args)
{
Collection<Integer> c = new ArrayList<Integer>();
for (int i = 0; i < 10; i++)
{
c.add(i);
}
for (int i : c)
{
System.out.println(i);
}
}
}

11.3 添加一组元素

Arrays.asList()方法接受一个数组或一个用逗号分隔的元素列表,并将其转换为一个List对象。Collections.addAll()方法接受一个Collection对象,以及一个数组或一个用逗号分隔的列表,将元素添加到Collection中。

import java.util.*;
public class AddingGroups
{
public static void main(String[] args)
{
Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
Integer[] moreInts = {6, 7, 8, 9};
collection.addAll(Arrays.asList(moreInts));
Collections.addAll(collection,12,13,14); Collections.addAll(collection,moreInts);
List<Integer> list=Arrays.asList(13,14,15);
}
}

Collection.addAll()成员方法只能接受一个Collection对象作为参数,因此不如Arrays.asList()和Collections.addAll()灵活,着两个方法都可以是可变参数。

可以直接使用Arrays.asList()的输出,将它当成List,但在这种情况下,地城是数组。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; public class AsListInference
{
public static void main(String[] args)
{
List<Snow> snow1 = Arrays.asList(new P(), new S());
List<Snow> snow2 = Arrays.asList(new P(), new S(), new L());
List<Snow> snow3 = Arrays.asList(new H(), new L());
List<Snow> snow4 = new ArrayList<Snow>();
Collections.addAll(snow4, new P(), new L());
List<Snow> snow5 = Arrays.<Snow>asList(new L());//显示类型参数说明
}
} class Snow{} class P extends Snow{} class L extends P{} class H extends P{} class S extends Snow{}

11.4 容器的打印

import java.util.*;

public class PrintingContainers
{
static Collection fill(Collection<String> coll)
{
coll.add("rat");
coll.add("cat");
return coll;
} static Map fill(Map<String, String> map)
{
map.put("rat", "r");
map.put("cat", "c");
return map;
} public static void main(String[] args)
{
System.out.println(fill(new ArrayList<String>()));
System.out.println(fill(new LinkedList<String>()));
System.out.println(fill(new HashSet<String>()));
System.out.println(fill(new TreeSet<String>()));
System.out.println(fill(new LinkedHashSet<String>())); System.out.println(fill(new HashMap<String,String>()));
System.out.println(fill(new TreeMap<String,String>()));
System.out.println(fill(new LinkedHashMap<String,String>()));
}
}

Collection在每个槽中只能保存一个元素,此类容器还包括List。Map每个槽内保存两个元素,键和值相关联的值。

ArrayList和LinkedList都是List类型,从输出可以看出,它们都是按照被插入的顺序保存元素的。

两者的不同之处不仅在于执行某写类型的操作时的性能。而且LinkedList包括的操作也多于ArrayList。

HashSet、TreeSet和LinkedHashSet都是Set类型。

Map使得你可以用键来查找对象,就像一个简单的数据库。键所关联的对象称为值。

HashSet一样,HashMap也提供了最快的查找技术,也没有按照任何明显的顺序来保存元素。TreeMap按照比较结果的升序保存键,而LinkedHashMap则按照插入顺序保存键,同时还保留了HashMap的查询速度。

11.5 List

List可以将元素维护在特定的序列中。List接口在Collection的基础上添加大量的方法,使得可以在List的中间插入和移除元素。

有两种类型的List:

  • 基本的ArrayList,它长于随机访问元素,但使在List的中间插入和移除元素时较慢。
  • LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问。LinkedList在随机访问方面比较慢,但是它的特性集较ArrayList更大。

11.6 迭代器

持有事物是容器最基本的工作,要使用容器,必须对容器的确切类型编程。

如果只要使用容器,不知道或者不关心容器的类型,要如何不重写代码就可以应用于不同类型的容器?

使用迭代器:迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不需要指定序列底层的结构。迭代器通常被称为轻量级对象:创建它的代价很小,经常可以看到对迭代器的奇怪限制。

比如Java中的Iterator只能单向移动。


接受对象容器并传递它,从而再每个对象上都执行操作。

创建一个display方法,不必知道容器的确切类型:


display方法不包含任何有关它遍历的序列的类型信息:能够将遍历序列的操作与序列底层的结构分离。所以,迭代器统一了对容器的访问方式。

11.6.1 ListIterator

ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类访问。但ListIterator可以双向移动。


11.7 LinkedList

LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作比ArrayList更高效,但再随机访问操作要差一些。


11.8 Stack

栈通常是指后进先出的容器。有时也被称为叠加栈,因为最后压入栈的元素,第一个弹出栈。

直接将LinkedList作为栈使用。

import java.util.LinkedList;

public class Stack<T>
{
LinkedList<T> storage=new LinkedList<T>();
public void push(T t){storage.addFirst(t);}//插入一个元素
public T peek(){return storage.getFirst();}//获取第一个元素
public T pop(){return storage.removeFirst();}//移除第一个元素
public boolean empty(){return storage.isEmpty();}//是否还有元素
public String toString(){return storage.toString();}
}

创建一个新的Stack类:

public class StackTest
{
public static void main(String[] args){
Stack<String> stack=new Stack<String>();
for(String s:"My dog has fleas".split(" "))
stack.push(s);
while (!stack.empty())
System.out.println(stack.pop());
}
}

11.9 Set

Set不保存重复的元素。

Set具有与Collection完全一样的接口。实际上Set就是Collection,只是行为不同。Set基于对象的的值来确定归属性。

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet; public class SetOfInteger
{
public static void main(String[] args){
Random rand=new Random(47);
Set<Integer> intset=new HashSet<Integer>();
for (int i=0;i<10;i++)
intset.add(rand.nextInt(30));
for(int i:intset)
System.out.println(i);
}
}

HashSet所维护的顺序与TreeSet或LinkedHashSet都不同。TreeSetj将元素存储再红——黑树数据结构中,而HashSet使用的是散列函数。LinkedHashList因为查询速度的原因也使用了散列。

对结构排序,使用TreeSet来代替HashSet

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet; public class SetOfInteger
{
public static void main(String[] args){
Random rand=new Random(47);
Set<Integer> intset=new TreeSet<Integer>();
for (int i=0;i<10;i++)
intset.add(rand.nextInt(30));
for(int i:intset)
System.out.println(i);
}
}

使用contains()测试Set归属性

import java.util.Collections;
import java.util.HashSet;
import java.util.Set; public class SetOperations
{
public static void main(String[] args){
Set<String> set1=new HashSet<String>();
Collections.addAll(set1,"a b c d e f g".split(" "));
set1.add("m");
System.out.println("m:"+set1.contains("m"));
System.out.println("h:"+set1.contains("h"));
}
}

打开文件,并将其读入一个Set中。

TextFile继承自List< Sting >,其构造器将打开文件,并更具正则表达式将其断开。

如果要按照字母排序,可以向TreeSet构造器传入String.CASE_INSENTIVE_ORDER比较器。

import net.mindview.util.TextFile;

import java.util.Set;
import java.util.TreeSet; public class UniqueWordsAlphabetic
{
public static void main(String[] args){
Set<String> words=new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
words.addAll(new TextFile("第十一章持有对象\\src\\SimpleCollection.java","\\W+"));
System.out.println(words);
}
}

11.10 Map

将对象映射到其他对象的能力是一种解决编程问题的杀手锏。

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class MapOfList
{
public static Map<Integer, List<? extends Person>> petPeople = new HashMap<Integer, List<? extends Person>>();
static {
petPeople.put(1, Arrays.asList(new Man("aaa"),new WoMan("bbb")));
}
public static void main(String[] args){
System.out.println(petPeople.keySet());
System.out.println(petPeople.values());
System.out.println(petPeople);
}
}
class Person
{
private String name;
Person(String name){
this.name=name;
}
}
class Man extends Person
{
public Man(String name){
super(name);
}
}
class WoMan extends Person
{
WoMan(String name){
super(name);
}
}

Map可以返回它的键的Set,它的值Collection,或者它的键值对的Set。

11.11 Queue

队列是一个典型的先进先出的容器。队列在并发编程中特别重要,因为它们可以安全地将对象从一个任务传输给另一个任务。

LinkedLisk提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedLisk可以用作Queue的一种实现。

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random; public class QueueDemo
{
public static void printQ(Queue queue){
while (queue.peek()!=null)
System.out.print(queue.remove()+" ");
System.out.println();
}
public static void main(String[] args){
Queue<Integer> queue=new LinkedList<Integer>();
Random ran=new Random();
for(int i=0;i<10;i++)
queue.offer(ran.nextInt(i+10));
printQ(queue);
Queue<Character> qc=new LinkedList<Character>();
for (char c:"Brontosaurus".toCharArray())
qc.offer(c);
printQ(qc);
}
}

offer()方法将一个元素插入到队尾,或者返回false。peek()和element()都将在不移除的情况下返回对头。poll()和remove()方法将移除并返回对头。

11.11.1 PriorityQueue

优先级队列声明下一个弹出元素最需要的元素。

当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认排序将使用对象在队列中自然顺序,但你可以通过提供自己的Comparator来修改这个顺序。

import java.util.*;

public class PriorityQueueDemo
{
public static void main(String[] args)
{
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
Random rand = new Random(47);
for (int i = 0; i < 10; i++)
priorityQueue.offer(rand.nextInt(i + 10));
QueueDemo.printQ(priorityQueue); List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2, 3);
priorityQueue = new PriorityQueue<Integer>(ints);
QueueDemo.printQ(priorityQueue); priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.reverseOrder());
priorityQueue.addAll(ints);
QueueDemo.printQ(priorityQueue); String face = "EDUCATION SHOULD ESCHEW";
List<String> strings = Arrays.asList(face.split(" "));
PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);
QueueDemo.printQ(stringPQ); stringPQ = new PriorityQueue<String>(strings.size(), Collections.reverseOrder());
stringPQ.addAll(strings);
QueueDemo.printQ(stringPQ); Set<Character> characters = new HashSet<Character>();
for (char c : face.toCharArray())
characters.add(c);
PriorityQueue<Character> characterspc = new PriorityQueue<Character>(characters);
QueueDemo.printQ(characterspc);
} }

在PriorityQueue重复是允许的,最小值拥有最高优先级。

11.12 Collection和Iterator

Collection是描述所以有序列容器的共性的根接口,它可能会被认为是一个附属接口,即因为要表示其他若干接口的共性出现的接口。

通过针对接口而非具体实现编写代码,我们的代码可以应用于更多的对象类型。

import java.util.*;

public class InterfaceVsIterator
{
public static void display(Iterator<Integer> it)
{
while (it.hasNext())
{
Integer i = it.next();
System.out.print(i+",");
}
System.out.println();
}
public static void display(Collection<Integer> ints)
{
for(Integer i:ints)
System.out.print(i+"-");
System.out.println();
}
public static void main(String[] args)
{
List<Integer> intList= Arrays.asList(1,2,3,15,3);
Set<Integer> intSet=new HashSet<Integer>(intList);
Map<String,Integer> intmap=new LinkedHashMap<String,Integer>();
String[] s=new String[]{"A","b","c","f","w"};
for(int i=0;i<s.length;i++)
intmap.put(s[i],intList.get(i));
display(intList);
display(intSet);
display(intList.iterator());
System.out.println(intmap);
System.out.println(intmap.keySet());
display(intmap.values());
display(intmap.values().iterator());
}
}

Collection和Iterator都可以将display()方法与底层容器的特定实现解耦。

import java.util.AbstractCollection;
import java.util.Iterator; public class CollectionSequence extends AbstractCollection<Integer>
{
private Integer[] ints=new Integer[]{1,4,2,5,1};
@Override
public Iterator<Integer> iterator()
{
return new Iterator<Integer>()
{
private int index=0;
@Override
public boolean hasNext()
{
return index<ints.length;
} @Override
public Integer next()
{
return ints[index++];
}
};
}
@Override
public int size()
{
return ints.length;
} public static void main(String[] args){
CollectionSequence c=new CollectionSequence();
InterfaceVsIterator.display(c);
InterfaceVsIterator.display(c.iterator());
}
}

生成Iterator是将队列与消费队列的方法连接在一起耦合度最小的方式,并且与1实现Collection相比,它在序列类上所加的约束少得多。

import java.util.Iterator;

public class NonCollectionSequence extends PetSequence
{
public Iterator<Integer> iterator()
{
return new Iterator<Integer>()
{
int index = 0; @Override
public boolean hasNext()
{
return index < ints.length;
} @Override
public Integer next()
{
return ints[index++];
}
};
}
public static void main(String[] args){
NonCollectionSequence nc=new NonCollectionSequence();
InterfaceVsIterator.display(nc.iterator());
}
} class PetSequence
{
protected int[] ints = new int[]{1, 4, 2, 5, 3};
}

11.13 Foreach与迭代器

foreach语法主要用于数组,但它也可以用于任何Collection对象。

Java SE5引入了新的被称为Iterable接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此,如果你创建了一个Iterable的类,都可以用于foreach。

import java.util.Iterator;

public class IterableClass implements Iterable<String>
{
protected String[] words = ("a,d,fe,r,gd,v,g").split(","); @Override
public Iterator<String> iterator()
{
return new Iterator<String>()
{
int index = 0; @Override
public boolean hasNext()
{
return index < words.length;
} @Override
public String next()
{
return words[index++];
}
};
}
public static void main(String[] args){
for(String s:new IterableClass())
System.out.print(s+",");
}
}

在Java SE中,大量的类都是Iterable类型,主要包括所有Collection类(不包括各种Map)

foreach可以用于数组或其他任何Iterable,但是这并不意味着数组肯定也是一个Iterable,二任何自动包装也不会发生。

import java.util.Arrays;

public class ArrayIsMotIterable
{
static<T> void test(Iterable<T> ib){
for(T t:ib)
System.out.print(t+" ");
}
public static void main(String[] args){
test(Arrays.asList(1,2,3,2));
String[] strings={"a","b","c"};
//test(strings);//转换失败
test(Arrays.asList(strings));
}
}

尝试把数组转换成Iterable会失败,不存在任何数组到Iterable的自动转换,必须手工执行这种转换。

11.13.1 适配器方法惯用法

希望在默认向前的迭代器基础上,添加产生反向迭代器的能力,因此,不能使用覆盖,需要添加一个能够产生Iterable对象的方法,该对象可以用于foreach语句。

mport java.util.Iterator;

public class IterableClass implements Iterable<String>
{
protected String[] words = ("a,d,fe,r,gd,v,g").split(","); @Override
public Iterator<String> iterator()
{
return new Iterator<String>()
{
int index =words.length-1 ; @Override
public boolean hasNext()
{
return index > -1;
} @Override
public String next()
{
return words[index--];
}
};
}
public static void main(String[] args){
for(String s:new IterableClass())
System.out.print(s+",");
} }

如果直接将ral对象置于foreach语句中,将得到向前迭代器。但如果在对象上调用产生向后迭代器的方法,就会产生不同的行为了。

通过这种方式,可以添加两种适配器方法。

Java编程思想之十一 持有对象的更多相关文章

  1. <Java编程思想>读书笔记(1)-对象导论、一切都是对象

    1.面向对象编程:OOP (Object-oriented Programming) 2.Alan Kay 总结的面向对象语言5个基本特性: 1) 万物皆为对象 2) 程序是对象的集合,他们通过发送消 ...

  2. 《Java编程思想》读书笔记-对象导论

    计算机是头脑延伸的工具,是一种不同类型的表达媒体.本文以背景性的和补充性的材料,介绍包括开发方法概述在内的面向对象程序设计(Object-oriented Programming,OOP)的基本概念. ...

  3. 2.JAVA编程思想——一切都是对象

    一切都是对象 欢迎转载.转载请标明出处:http://blog.csdn.net/notbaron/article/details/51040221 虽然以C++为基础,但 Java 是一种更纯粹的面 ...

  4. java编程思想(1)--对象导论

    对象导论: 1.1 抽象过程 所有的语言都有抽象机制,抽象是解决复杂问题的根本方法.例如:汇编语言是对底层机器的轻微抽象.命令式语言(如:FORTRAN.BASIC.C)又是对汇编语言的抽象. jav ...

  5. Java编程思想(第一章 对象入门)总结

    面向对象编程(oop) 1.1抽象的进步 所有编程语言的最终目的都是提供一种“抽象”方法.   难点是 在机器模型(位于“方案空间”)和实际解决问题模型(位于“问题空间”)之间,程序员必须建立起一种联 ...

  6. java编程思想 一切都是对象

    java是一种面向对象程序设计语言,一切都是对象,并且用引用操作对象,如一个电视机就是一个对象,而操作电视机的遥控器就是引用,引用可以单独存在,如遥控器可以单独存在. String s; 这里只是创建 ...

  7. Java编程思想学习(十一) 泛型

    1.概要 generics enable types (classes and interfaces) to be parameters when defining classes, interfac ...

  8. Java编程思想阅读收获

    15年8月份买了一本Java编程思想第四版中文版.之所以买中文版是因为我试读了同事的英文版发现自己英语水平还是有限,单词虽然认识,但对很多句子把握不准,这样看书太慢了,要理解英文还要理解技术有些hol ...

  9. Java编程思想读书笔记(一)【对象导论】

    2018年1月7日15:45:58 前言 作为学习Java语言的经典之作<Java编程思想>,常常被人提起.虽然这本书出版十年有余,但是内容还是很给力的.很多人说这本书不是很适合初学者,我 ...

随机推荐

  1. SqlServer数据库分区分表实例分享(有详细代码和解释)

    数据库单表数据量太大可能会导致数据库的查询速度大大下降(感觉都是千万级以上的数据表了),可以采取分区分表将大表分为小表解决(当然这只是其中一种方法),比如数据按月.按年分表,最后可以使用视图将小表重新 ...

  2. STM32最小系统设计

    STM32最小系统设计 概述 最近在在设计一块板子的时候发现在设计STM32电路这部分时,有些东西模棱两可.本着科学严谨的态度,本着对工作负责的态度(板子设计坏了都是money!),这里对STM32最 ...

  3. Robotframework ride ,运行后提示, [WinError 2] 系统找不到指定的文件。

    运行后提示, [WinError 2] 系统找不到指定的文件. command: pybot.bat --argumentfile C:\Users\123\AppData\Local\Temp\RI ...

  4. 【JZOJ】2126. 最大约数和

    题目大意 选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大. 分析 把我们分解出来的因数进行合并,存在一个不知名的数组里,然后我们大可开始我们的迪屁!!(bag),我们可以 ...

  5. json串处理2

    请求百度地址坐标:http://api.map.baidu.com/location/ip?ak=y0Yb5ZgGK9blTDbR7Dwh9jGtn6X1YE48&coor=bd09ll&am ...

  6. iOS批量添加SDK自动打包GUI工具

    背景 1.之前在给游戏开发商做SDK接入技术支持的时候,很多cp对iOS开发技术并不是很了解,对接SDK和打包都很迷糊,虽然我们根据他们的开发环境输出了不同的插件解决方案,这一步已经把接入SDK的复杂 ...

  7. contab路径问题(脚本调用另一个文件)

    问题描述 当在定时任务里,要执行一个脚本A,然后A脚本需要调用另一个文件B,此时定时任务执行不成功,会报错找不到文件   解决办法 先cd到放执行脚本的路径,这样就在定时任务的脚本里可以调用相对路径下 ...

  8. 数据库系统(四)---关系型数据库设计及E-R图

    1.关系型数据库: 关系型数据库是一类采用关系模型作为逻辑数据模型的数据库系统,遵从数据库设计的基本步骤,包括:需求分析.概念结构设计.逻辑结构设计.物理结构设计.数据库实施.数据库的运行和维护等阶段 ...

  9. Linux shell 中断循环语句

    无限循环: 循环有限的生命,他们跳出来,一旦条件是 false 还是 false 取决于循环. 由于所需的条件是不符合一个循环可能永远持续下去.永远不会终止执行一个循环执行无限次数.出于这个原因,这样 ...

  10. pycharm Launching unittests with arguments

    在运行程序时出现 但是代码没有错 源代码是: 这是运行时启动了测试 解决方法: File-> Settings -> Tools -> Python Integrated Tools ...