第17章 容器深入研究
17.2 填充容器

package com.cy.container;

import java.util.*;

class StringAddress {
private String s; public StringAddress(String s) {
this.s = s;
}
public String toString() {
return super.toString() + " " + s;
}
} public class FillingLists {
public static void main(String[] args) {
List<StringAddress> list= new ArrayList<StringAddress>(Collections.nCopies(4, new StringAddress("Hello")));
System.out.println(list); Collections.fill(list, new StringAddress("World!"));
System.out.println(list);
}
} /* Output: (Sample)
[StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello]
[StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!]
*///:~
这个示例展示了两种用对单个对象的引用来填充Collection的方式,第一种是使用
Collections.nCopies()创建传递给构造器的List,这里填充的是ArrayList.
StringAddress的toString()方法调用Object.toString()并产生该类的名字,后面紧跟该对象的
散列码的无符号十六进制表示.(通过hashCode()生成的)。从输出中你可以看到所有引用都被设
置为指向相同的对象,在第二种方法的Collection.fill()被调用之后也是如此。fill()方法的用处更
有限,因为它只能替换已经在List中存在的元素,而不能添加新的元素。
 
17.4.1 未获支持的操作
最常见的未获支持的操作,都来源于背后由固定尺寸的数据结构支持的容器。当你用
Arrays.asList()将数组转换为List时,就会得到这样的容器。你还可以通过使用Collections类中
"不可修改"的方法,选择创建任何会抛出UnsupportedOperationException的容器(包括Map ) 。
下面的示例包括这两种情况:
package com.cy.container;

import java.util.*;

public class Unsupported {
static void test(String msg, List<String> list) {
System.out.println("--- " + msg + " ---");
Collection<String> c = list;
Collection<String> subList = list.subList(1,8);
// Copy of the sublist:
Collection<String> c2 = new ArrayList<String>(subList);
try { c.retainAll(c2); } catch(Exception e) {
System.out.println("retainAll(): " + e);
}
try { c.removeAll(c2); } catch(Exception e) {
System.out.println("removeAll(): " + e);
}
try { c.clear(); } catch(Exception e) {
System.out.println("clear(): " + e);
}
try { c.add("X"); } catch(Exception e) {
System.out.println("add(): " + e);
}
try { c.addAll(c2); } catch(Exception e) {
System.out.println("addAll(): " + e);
}
try { c.remove("C"); } catch(Exception e) {
System.out.println("remove(): " + e);
}
// The List.set() method modifies the value but
// doesn't change the size of the data structure:
try {
list.set(0, "X");
} catch(Exception e) {
System.out.println("List.set(): " + e);
}
} public static void main(String[] args) {
List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));
test("Modifiable Copy", new ArrayList<String>(list));
test("Arrays.asList()", list);
test("unmodifiableList()", Collections.unmodifiableList(new ArrayList<String>(list)));
}
} /* Output:
--- Modifiable Copy ---
--- Arrays.asList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- unmodifiableList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
List.set(): java.lang.UnsupportedOperationException
*///:~
因为Arrays.asList()会生成一个List,它基于一个固定大小的数组, 仅支持那些不会改变数组
大小的操作,对它而言是有道理的。任何会引起对底层数据结构的尺寸进行修改的方法都会产生
一个UnsupportedOperationException异常,以表示对未获支持操作的调用(一个编程错误).
注意,应该把Arrays.asList()的结果作为构造器的参数传递给任何Collection (或者使用
addAll()方法,或Collections.addAll()静态方法) ,这样可以生成允许使用所有的方法的普通容
器一一这在main()中的第一个对test()的调用中得到了展示, 这样的调用会产生新的尺寸可调的
底层数据结构。Collections类中的"不可修改"的方法将容器包装到了一个代理中,只要你执
行任何试图修改容器的操作,这个代理都会产生UnsupportedOperationException异常。使用这
些方怯的目标就是产生"常量"容器对象。"不可修改"的Collections方法的完整列表将在稍后
介绍。
test()中的最后一个try语句块将检查作为List的一部分的set()方法.这很有趣,因为你可以
看到"未获支持的操作"这一技术的粒度来的是多么方便-一所产生的"接口"可以在
Arrays.asList()返回的对象和Collections.unmodifiableList()返回的对象之间,在一个方法的粒
度上产生变化。Arrays.asList()返回固定尺寸的List,而Collections.unmodifiableList()产生不可
修改的列表.正如从输出中所看到的.修改Arrays.asList()返回的List中的元素是可以的,因为
这没有违反该List“尺寸固定"这一特性.但是很明显, unmodlfiableList()的结果在任何情况
下都应该不是可修改的。如果使用的是接口,那么还需要两个附加的接口,一个具有可以工作
的set()方法,另外一个没有, 因为附加的接口对于Collection的各种不可修改的子类型来说是必
需的。
 
 

17.6 Set和存储顺序
在HashSet上打星号表示,如果没有其他的限制,这就应该是你默认的选择,因为它对速度
进行了优化。
定义hashCode()的机制将在本章稍后进行介绍。你必须为散列存储和树型存储都创建一个
equals()方法,但是hashCode()只有在这个类将会被置于HashSet (这是有可能的,因为它通常
是你的Set实现的首选)或者LinkedHashSet中时才是必需的。但是,对于良好的编程风格而言,
你应该在覆盖equals()方法时,总是同时覆盖hashCode()方法。
package com.cy.container;

import java.util.*;

class SetType {
int i;
public SetType(int n) {
i = n;
}
public boolean equals(Object o) {
return o instanceof SetType && (i == ((SetType)o).i);
}
public String toString() {
return Integer.toString(i);
}
} class HashType extends SetType {
public HashType(int n) { super(n); }
public int hashCode() {
return this.i;
}
} class TreeType extends SetType implements Comparable<TreeType> {
public TreeType(int n) { super(n); } @Override
public int compareTo(TreeType arg) {
return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));
}
} public class TypesForSets {
static <T> Set<T> fill(Set<T> set, Class<T> type) {
try {
for(int i = 0; i < 10; i++){
T t = type.getConstructor(int.class).newInstance(i);
set.add(t);
}
} catch(Exception e) {
throw new RuntimeException(e);
}
return set;
}
static <T> void test(Set<T> set, Class<T> type) {
fill(set, type);
fill(set, type); // Try to add duplicates
fill(set, type);
System.out.println(set);
}
public static void main(String[] args) {
test(new HashSet<HashType>(), HashType.class);
test(new LinkedHashSet<HashType>(), HashType.class);
test(new TreeSet<TreeType>(), TreeType.class); // Things that don't work:
test(new HashSet<SetType>(), SetType.class);
test(new HashSet<TreeType>(), TreeType.class);
test(new LinkedHashSet<SetType>(), SetType.class);
test(new LinkedHashSet<TreeType>(), TreeType.class); try {
test(new TreeSet<SetType>(), SetType.class);
} catch(Exception e) {
System.out.println(e.getMessage());
}
try {
test(new TreeSet<HashType>(), HashType.class);
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} /* Output: (Sample)
[2, 4, 9, 8, 6, 1, 3, 7, 5, 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
[9, 9, 7, 5, 1, 2, 6, 3, 0, 7, 2, 4, 4, 7, 9, 1, 3, 6, 2, 4, 3, 0, 5, 0, 8, 8, 8, 6, 5, 1]
[0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2, 1, 8, 2, 8, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
java.lang.ClassCastException: SetType cannot be cast to java.lang.Comparable
java.lang.ClassCastException: HashType cannot be cast to java.lang.Comparable
*///:~
HashType继承自SetType , 并且添加丁hashCode()方法,该方法对于放置到Set的散列实现
中的对象来说是必需的。
TreeType实现了Comparable接口,如果一个对象被用于任何种类的排序容器中,例如
SortedSet (TreeSet是其唯一实现) ,那么它必须实现这个接口。
从输出中可以看到, HashSet以某种神秘的顺序保存所有的元素(这将在本章稍后进行解释) ,
LinkedHashSet按照元素插入的顺序保存元素,而TreeSet按照排序顺序维护元素(按照
compareTo()的实现方式,这里维护的是降序)。
如果我们尝试着将没有恰当地支持必需的操作的类型用于需要这些方法的Set ,那么就会有
大麻烦了。对于没有重新定义hashCode()方法的SetType或TreeType,如果将它们放置到任何散
列实现中都会产生重复值,这样就违反了Set的基本契约。这相当烦人,因为这甚至不会有运行
时错误。但是,默认的hashCode()是合法的,因此这是合法的行为,即便它不正确。确保这种程
序的正确性的唯一可靠方法就是将单元测试合并到你的构建系统中.
如果我们尝试着在TreeSet中使用没有实现Comparable的类型,那么你将会得到更确定的结
果: 在TreeSet试图将该对象当作Comparable使用时,将抛出一个异常。
 
 
17.6.1 SortedSet
SortedSet中的元素可以保证处于排序状态,这使得它可以通过在SortedSet接口中的下列方
法提供附加的功能:
Comparator comparator()返回当前Set使用的Comparator , 或者返回null ,表示以自然方式排序。
Object flrst() 返回容器中的第一个元素。
Object last() 返回容器中的最末一个元素。
SortedSet subSet(fromElement, toElement) 生成此Set的子集, 范围从fromElement (包含)到toEIement (不包含)。
SortedSet headSet(toElement) 生成此Set的子集,由小于toElement的元素组成。
SortedSet tailSet(fromElement) 生成此Set的子集,由大于或等于fromElement的元素组成。
package com.cy.container;

import java.util.*;
import static com.java.util.Print.*; public class SortedSetDemo {
public static void main(String[] args) {
SortedSet<String> sortedSet = new TreeSet<String>();
Collections.addAll(sortedSet,"one two three four five six seven eight".split(" "));
print(sortedSet); String low = sortedSet.first();
String high = sortedSet.last();
print(low);
print(high); Iterator<String> it = sortedSet.iterator();
for(int i = 0; i <= 6; i++) {
if(i == 3) low = it.next();
if(i == 6) high = it.next();
else it.next();
}
print(low);
print(high);
print(sortedSet.subSet(low, high));
print(sortedSet.headSet(high));
print(sortedSet.tailSet(low));
}
}
/* Output:
[eight, five, four, one, seven, six, three, two]
eight
two
one
two
[one, seven, six, three]
[eight, five, four, one, seven, six, three]
[one, seven, six, three, two]
*///:~
注意, SortedSet的意思是"按对象的比较函数对元素排序",而不是指"元素插入的次序"。
插入顺序可以用LinkedHashSet来保存。
17.7 队列
除了并发应用, Queue在Java SE5中仅有的两个实现是LinkedList和PriorityQueue ,它们的
差异在于排序行为而不是性能。下面是涉及Queue实现的大部分操作的基本示例(并非所有的
操作在本例中都能工作) ,包括基于并发的Queue。你可以将元素从队列的一端插入,并于另
端将它们抽取出来:
package com.cy.container;

import java.util.concurrent.*;
import java.util.*;
import com.java.util.*; public class QueueBehavior {
private static int count = 10; static <T> void test(Queue<T> queue, Generator<T> gen) {
for(int i = 0; i < count; i++){
queue.offer(gen.next());
} while(queue.peek() != null){
System.out.print(queue.remove() + " ");
} System.out.println();
} static class Gen implements Generator<String> {
String[] s = ("one two three four five six seven eight nine ten").split(" ");
int i;
public String next() {
return s[i++];
}
} public static void main(String[] args) {
test(new LinkedList<String>(), new Gen());
test(new PriorityQueue<String>(), new Gen());
test(new ArrayBlockingQueue<String>(count), new Gen());
test(new ConcurrentLinkedQueue<String>(), new Gen());
test(new LinkedBlockingQueue<String>(), new Gen());
test(new PriorityBlockingQueue<String>(), new Gen());
}
} /* Output:
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
*///:~
package com.java.util;

public interface Generator<T> {
T next();
}
你可以看到,除了优先级队列,Queue将精确地按照元素被置于Queue中的顺序产生它们.
 
1 7.7.1 优先级队列
在第11章曾经绘出过优先级队列的一个简单介绍.其中更有趣的问题是to-do列表,该列表
中每个对象都包含一个字符串和一个主要的以及次要的优先级值。该列表的排序顺序也是通过
实现Comparable而进行控制的:
package com.cy.container;

import java.util.*;

class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
static class ToDoItem implements Comparable<ToDoItem> {
private char primary;
private int secondary;
private String item; public ToDoItem(String td, char pri, int sec) {
primary = pri;
secondary = sec;
item = td;
} public int compareTo(ToDoItem arg) {
if(primary > arg.primary)
return +1;
if(primary == arg.primary)
if(secondary > arg.secondary)
return +1;
else if(secondary == arg.secondary)
return 0;
return -1;
} public String toString() {
return Character.toString(primary) + secondary + ": " + item;
}
} public void add(String td, char pri, int sec) {
super.add(new ToDoItem(td, pri, sec));
} public static void main(String[] args) {
ToDoList toDoList = new ToDoList();
toDoList.add("Empty trash", 'C', 4);
toDoList.add("Feed dog", 'A', 2);
toDoList.add("Feed bird", 'B', 7);
toDoList.add("Mow lawn", 'C', 3);
toDoList.add("Water lawn", 'A', 1);
toDoList.add("Feed cat", 'B', 1); while(!toDoList.isEmpty())
System.out.println(toDoList.remove());
}
}
/* Output:
A1: Water lawn
A2: Feed dog
B1: Feed cat
B7: Feed bird
C3: Mow lawn
C4: Empty trash
*///:~
你可以看到各个项的排序是如何因为使用了优先级队列而得以自动发生的.
 
 
 

-----------------

ThinkJava-容器深入研究的更多相关文章

  1. 容器技术研究-Kubernetes基本概念

    最近在研究容器技术,作为入门,基本概念必须搞明白,今天整理一下Kubernetes的基本概念. 一.什么是Kubernetes Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部 ...

  2. 《Think in Java》(十七)容器深入研究

    阿西吧,这一章好长啊,感觉看了快一个月了吧!JDK 自带的容器框架真是很好很强大啊,这一章看得有点蒙蒙的,接下来还得去看看官方文档啊!

  3. 《Java编程思想》笔记 第十七章 容器深入研究

    1 容器分类 容器分为Collection集合类,和Map键值对类2种 使用最多的就是第三层的容器类,其实在第三层之上还有一层Abstract 抽象类,如果要实现自己的集合类,可以继承Abstract ...

  4. Thinking in Java:容器深入研究

    1.虚线框表示Abstract类,图中大量的类的名字都是以Abstract开头的,它们仅仅是部分实现了特定接口的工具,因此创建时能够选择从Abstract继承. Collections中的实用方法:挑 ...

  5. Java编程思想之十七 容器深入研究

    17.1 完整的容器分类方法 17.2 填充容器 import java.util.*; class StringAddress { private String s; public StringAd ...

  6. spring源码分析系列3:BeanFactory核心容器的研究

    目录 @(spring源码分析系列3:核心容器的研究) 在讲容器之前,再明确一下知识点. BeanDefinition是Bean在容器的描述.BeanDefinition与Bean不是一个东西. Be ...

  7. Java编程思想——第17章 容器深入研究 读书笔记(四)

    九.散列与散列码 HashMap使用equals()判断当前的键是否与表中存在的键相同. 正确的equals()方法需满足一下条件: 1)自反性.x.equals(x) 是true; 2)对称性.x. ...

  8. Java编程思想——第17章 容器深入研究 读书笔记(三)

    七.队列 排队,先进先出. 除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: ad ...

  9. Java编程思想——第17章 容器深入研究 读书笔记(一)

    这一章将学习散列机制是如何工作的,以及在使用散列容器时怎么样编写hashCode()和equals()方法. 一.容器分类 先上两张图 来概况完整的容器分类 再细说都为什么会有那些特性. 二.填充容器 ...

  10. Java编程思想——第17章 容器深入研究(two)

    六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add ...

随机推荐

  1. delphi中TQueue的使用问题

    TQueue里存放的是指针,所要存储的内容最好建立在堆上,在pop方法之后释放掉这个空间. 实例代码: MMSQueue:= TQueue.Create; MMSQueue.Push(StrNew(P ...

  2. js写的一个HashMap

    1.脚本 /** * 模拟HashMap */ function HashMap(){ //定义长度 var length = 0; //创建一个对象 var obj = new Object(); ...

  3. jQuery的noConflict以及插件扩展

    一.noConflict函数 JavaScript有很多插件,如果jQuery对象的$与其他插件冲突,我们可以使用noConflict()方法去掉$或者使用其他的符号代替 注:noConflict() ...

  4. pseudo tty破除无法自动输入密码的限制

    没有root权限,没有ssh密钥对,又想自动输入密码咋办? #!/usr/bin/python # simplest builtin python pseudo-tty for ssh passwor ...

  5. OC基础:getter和setter,@public @protected @private 分类: ios学习 OC 2015-06-15 19:23 22人阅读 评论(0) 收藏

    @public 1.公开的,公共的,可以在类的内部和外部访问. 2.类的内部:实例变量名 3.类的外部:对象->实例变量名 @protected 1.受保护的,只能在本类和子类中可以访问 2.类 ...

  6. cocos2dx 3.1.1移植安卓apk (lua项目交叉编译 mac环境下)

    cocos2dx 3.1.1 lua项目安卓交叉编译 mac环境下 本文基于ant,sdk,ndk,adt等软件和环境已经事前设置好 1\新建项目 在mac的终端下输入命令: cocos new te ...

  7. vue2.0分页组件,

    pagination.vue <!-- 表格分页组件 --> <template> <nav class="boot-nav"> <ul ...

  8. ubuntu16.04 下 NVIDIA GTX1050ti 显卡驱动 PPA安装

    本文参考资料链接: http://blog.csdn.net/10km/article/details/61191230 前几天在京东商城上花了6999元买了台笔记本(惠普(HP)暗影精灵II代Pro ...

  9. python 正则表达式 提取网页中标签的中文

    转载请注明出处 http://www.cnblogs.com/pengwang52/. >>> p= re.compile(r'\<div class="commen ...

  10. 压力测试命令行工具SuperBenchmarker

    压力测试命令行工具SuperBenchmarker SuperBenchmarker 是ㄧ个开源的类似于Apache ab的压力测试命令行工具.可以在 .NET 4.52+ 或者 .NET Core ...