- 浏览: 685169 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (254)
- java分布式应用架构 (22)
- SSH框架整合 (6)
- java web 学习笔记 (49)
- java 学习笔记 (56)
- struts 2 学习 (6)
- Hibernate学习 (10)
- spring 学习 (2)
- 客户端编程(javascript) (4)
- IDE使用 (13)
- 生命 人生 (6)
- 系统维护 (3)
- 技术篇 (10)
- MySql (2)
- J2ME (1)
- java网络编程 (4)
- 数据库 (5)
- C/C++ (8)
- Oracle (7)
- 软件测试 (0)
- 软件的安装和部署 (0)
- Java快讯 (1)
- swt (1)
- Flex (1)
- 软件工程 (1)
- PostgreSQL (1)
- sql server2000 (2)
- 嵌入式数据库sqlite (5)
- J2EE (1)
- XML (1)
- ibatis3(MyBatis) (6)
- Linux&Unix (1)
- velocity (1)
- 回报社会 (4)
- 软件项目管理 (3)
- android研究 (3)
- C# (2)
- Objective-C (1)
- 音乐 (0)
- webx (1)
- JMS (1)
- maven软件项目管理 (1)
- 分布式服务 (0)
- 云平台 (0)
- 分布式存储 (1)
- 分布式系统架构 (0)
- 移动互联网 (1)
- ZooKeeper (1)
最新评论
-
liyys:
楼主,可不可以发这个项目的源码工程出来分享一下,少了几个类。楼 ...
仿照Hibernate实现一个SQLite的ORM框架 -
liyys:
少了一些类的源码没有粘贴出来
仿照Hibernate实现一个SQLite的ORM框架 -
honglei0412:
我使用的是这种方式获取db文件的目录但是 URL p = Fi ...
使用sqlite注意事项 -
honglei0412:
大侠 能不能说明下DbFile您是怎么做的吗?
使用sqlite注意事项 -
ahack:
刚写完mapping才发现早就有人写好了。仔细一看还都是针对的 ...
仿照Hibernate实现一个SQLite的ORM框架
简介: 您可以在任何地方使用 Java™ 集合,但是一定要小心。集合有很多秘密,如果不正确处理可能会带来麻烦。Ted 探索了 Java Collections API 复杂、多变的一面并为您提供了一些技巧,帮您充分利用 Iterable、HashMap 和 SortedSet,又不会带来 bug。
java.util 中的 Collections 类旨在通过取代数组提高 Java 性能。如您在 第 1 部分 中了解到的,它们也是多变的,能够以各种方式定制和扩展,帮助实现优质、简洁的代码。
Collections 非常强大,但是很多变:使用它们要小心,滥用它们会带来风险。
1. List 不同于数组
Java 开发人员常常错误地认为 ArrayList 就是 Java 数组的替代品。Collections 由数组支持,在集合内随机查找内容时性能较好。与数组一样,集合使用整序数获取特定项。但集合不是数组的简单替代。
要明白数组与集合的区别需要弄清楚顺序 和位置 的不同。例如,List 是一个接口,它保存各个项被放入集合中的顺序,如清单 1 所示:
清单 1. 可变键值
当第三个元素从上面的 List 中被移除时,其 “后面” 的各项会上升填补空位。很显然,此集合行为与数组的行为不同(事实上,从数组中移除项与从 List 中移除它也不完全是一回事儿 — 从数组中 “移除” 项意味着要用新引用或 null 覆盖其索引槽)。
2. 令人惊讶的 Iterator!
无疑 Java 开发人员很喜爱 Java 集合 Iterator,但是您最后一次使用 Iterator 接口是什么时候的事情了?可以这么说,大部分时间我们只是将 Iterator 随意放到 for() 循环或加强 for() 循环中,然后就继续其他操作了。
但是进行深入研究后,您会发现 Iterator 实际上有两个十分有用的功能。
第一,Iterator 支持从源集合中安全地删除对象,只需在 Iterator 上调用 remove() 即可。这样做的好处是可以避免 ConcurrentModifiedException,这个异常顾名思意:当打开 Iterator 迭代集合时,同时又在对集合进行修改。有些集合不允许在迭代时删除或添加元素,但是调用 Iterator 的 remove() 方法是个安全的做法。
第二,Iterator 支持派生的(并且可能是更强大的)兄弟成员。ListIterator,只存在于 List 中,支持在迭代期间向 List 中添加或删除元素,并且可以在 List 中双向滚动。
双向滚动特别有用,尤其是在无处不在的 “滑动结果集” 操作中,因为结果集中只能显示从数据库或其他集合中获取的众多结果中的 10 个。它还可以用于 “反向遍历” 集合或列表,而无需每次都从前向后遍历。插入 ListIterator 比使用向下计数整数参数 List.get() “反向” 遍历 List 容易得多。
3. 并非所有 Iterable 都来自集合
Ruby 和 Groovy 开发人员喜欢炫耀他们如何能迭代整个文本文件并通过一行代码将其内容输出到控制台。通常,他们会说在 Java 编程中完成同样的操作需要很多行代码:打开 FileReader,然后打开 BufferedReader,接着创建 while() 循环来调用 getLine(),直到它返回 null。当然,在 try/catch/finally 块中必须要完成这些操作,它要处理异常并在结束时关闭文件句柄。
这看起来像是一个没有意义的学术上的争论,但是它也有其自身的价值。
他们(包括相当一部分 Java 开发人员)不知道并不是所有 Iterable 都来自集合。Iterable 可以创建 Iterator,该迭代器知道如何凭空制造下一个元素,而不是从预先存在的 Collection 中盲目地处理:
清单 2. 迭代文件
此方法的优势是不会在内存中保留整个内容,但是有一个警告就是,它不能 close() 底层文件句柄(每当 readLine() 返回 null 时就关闭文件句柄,可以修正这一问题,但是在 Iterator 没有结束时不能解决这个问题)。
4. 注意可变的 hashCode()
Map 是很好的集合,为我们带来了在其他语言(比如 Perl)中经常可见的好用的键/值对集合。JDK 以 HashMap 的形式为我们提供了方便的 Map 实现,它在内部使用哈希表实现了对键的对应值的快速查找。但是这里也有一个小问题:支持哈希码的键依赖于可变字段的内容,这样容易产生 bug,即使最耐心的 Java 开发人员也会被这些 bug 逼疯。
假设清单 3 中的 Person 对象有一个常见的 hashCode() (它使用 firstName、lastName 和 age 字段 — 所有字段都不是 final 字段 — 计算 hashCode()),对 Map 的 get() 调用会失败并返回 null:
清单 3. 可变 hashCode() 容易出现 bug
很显然,这种方法很糟糕,但是解决方法也很简单:永远不要将可变对象类型用作 HashMap 中的键。
5. equals() 与 Comparable
在浏览 Javadoc 时,Java 开发人员常常会遇到 SortedSet 类型(它在 JDK 中唯一的实现是 TreeSet)。因为 SortedSet 是 java.util 包中唯一提供某种排序行为的 Collection,所以开发人员通常直接使用它而不会仔细地研究它。清单 4 展示了:
清单 4. SortedSet,我很高兴找到了它!
使用上述代码一段时间后,可能会发现这个 Set 的核心特性之一:它不允许重复。该特性在 Set Javadoc 中进行了介绍。Set 是不包含重复元素的集合。更准确地说,set 不包含成对的 e1 和 e2 元素,因此如果 e1.equals(e2),那么最多包含一个 null 元素。
但实际上似乎并非如此 — 尽管 清单 4 中没有相等的 Person 对象(根据 Person 的 equals() 实现),但在输出时只有三个对象出现在 TreeSet 中。
与 set 的有状态本质相反,TreeSet 要求对象直接实现 Comparable 或者在构造时传入 Comparator,它不使用 equals() 比较对象;它使用 Comparator/Comparable 的 compare 或 compareTo 方法。
因此存储在 Set 中的对象有两种方式确定相等性:大家常用的 equals() 方法和 Comparable/Comparator 方法,采用哪种方法取决于上下文。
更糟的是,简单的声明两者相等还不够,因为以排序为目的的比较不同于以相等性为目的的比较:可以想象一下按姓排序时两个 Person 相等,但是其内容却并不相同。
一定要明白 equals() 和 Comparable.compareTo() 两者之间的不同 — 实现 Set 时会返回 0。甚至在文档中也要明确两者的区别。
结束语
Java Collections 库中有很多有用之物,如果您能加以利用,它们可以让您的工作更轻松、更高效。但是发掘这些有用之物可能有点复杂,比如只要您不将可变对象类型作为键,您就可以用自己的方式使用 HashMap。
java.util 中的 Collections 类旨在通过取代数组提高 Java 性能。如您在 第 1 部分 中了解到的,它们也是多变的,能够以各种方式定制和扩展,帮助实现优质、简洁的代码。
Collections 非常强大,但是很多变:使用它们要小心,滥用它们会带来风险。
1. List 不同于数组
Java 开发人员常常错误地认为 ArrayList 就是 Java 数组的替代品。Collections 由数组支持,在集合内随机查找内容时性能较好。与数组一样,集合使用整序数获取特定项。但集合不是数组的简单替代。
要明白数组与集合的区别需要弄清楚顺序 和位置 的不同。例如,List 是一个接口,它保存各个项被放入集合中的顺序,如清单 1 所示:
清单 1. 可变键值
#div_code img{border:0px;} <!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->import java.util.*; public class OrderAndPosition { public static <T> void dumpArray(T[] array) { System.out.println("============="); for (int i=0; i<array.length; i++) System.out.println("Position " + i + ": " + array[i]); } public static <T> void dumpList(List<T> list) { System.out.println("============="); for (int i=0; i<list.size(); i++) System.out.println("Ordinal " + i + ": " + list.get(i)); } public static void main(String[] args) { List<String> argList = new ArrayList<String>(Arrays.asList(args)); dumpArray(args); args[1] = null; dumpArray(args); dumpList(argList); argList.remove(1); dumpList(argList); } }
当第三个元素从上面的 List 中被移除时,其 “后面” 的各项会上升填补空位。很显然,此集合行为与数组的行为不同(事实上,从数组中移除项与从 List 中移除它也不完全是一回事儿 — 从数组中 “移除” 项意味着要用新引用或 null 覆盖其索引槽)。
2. 令人惊讶的 Iterator!
无疑 Java 开发人员很喜爱 Java 集合 Iterator,但是您最后一次使用 Iterator 接口是什么时候的事情了?可以这么说,大部分时间我们只是将 Iterator 随意放到 for() 循环或加强 for() 循环中,然后就继续其他操作了。
但是进行深入研究后,您会发现 Iterator 实际上有两个十分有用的功能。
第一,Iterator 支持从源集合中安全地删除对象,只需在 Iterator 上调用 remove() 即可。这样做的好处是可以避免 ConcurrentModifiedException,这个异常顾名思意:当打开 Iterator 迭代集合时,同时又在对集合进行修改。有些集合不允许在迭代时删除或添加元素,但是调用 Iterator 的 remove() 方法是个安全的做法。
第二,Iterator 支持派生的(并且可能是更强大的)兄弟成员。ListIterator,只存在于 List 中,支持在迭代期间向 List 中添加或删除元素,并且可以在 List 中双向滚动。
双向滚动特别有用,尤其是在无处不在的 “滑动结果集” 操作中,因为结果集中只能显示从数据库或其他集合中获取的众多结果中的 10 个。它还可以用于 “反向遍历” 集合或列表,而无需每次都从前向后遍历。插入 ListIterator 比使用向下计数整数参数 List.get() “反向” 遍历 List 容易得多。
3. 并非所有 Iterable 都来自集合
Ruby 和 Groovy 开发人员喜欢炫耀他们如何能迭代整个文本文件并通过一行代码将其内容输出到控制台。通常,他们会说在 Java 编程中完成同样的操作需要很多行代码:打开 FileReader,然后打开 BufferedReader,接着创建 while() 循环来调用 getLine(),直到它返回 null。当然,在 try/catch/finally 块中必须要完成这些操作,它要处理异常并在结束时关闭文件句柄。
这看起来像是一个没有意义的学术上的争论,但是它也有其自身的价值。
他们(包括相当一部分 Java 开发人员)不知道并不是所有 Iterable 都来自集合。Iterable 可以创建 Iterator,该迭代器知道如何凭空制造下一个元素,而不是从预先存在的 Collection 中盲目地处理:
清单 2. 迭代文件
#div_code img{border:0px;} <!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->// FileUtils.java import java.io.*; import java.util.*; public class FileUtils { public static Iterable<String> readlines(String filename) throws IOException { final FileReader fr = new FileReader(filename); final BufferedReader br = new BufferedReader(fr); return new Iterable<String>() { public <code>Iterator</code><String> iterator() { return new <code>Iterator</code><String>() { public boolean hasNext() { return line != null; } public String next() { String retval = line; line = getLine(); return retval; } public void remove() { throw new UnsupportedOperationException(); } String getLine() { String line = null; try { line = br.readLine(); } catch (IOException ioEx) { line = null; } return line; } String line = getLine(); }; } }; } } //DumpApp.java import java.util.*; public class DumpApp { public static void main(String[] args) throws Exception { for (String line : FileUtils.readlines(args[0])) System.out.println(line); } }
此方法的优势是不会在内存中保留整个内容,但是有一个警告就是,它不能 close() 底层文件句柄(每当 readLine() 返回 null 时就关闭文件句柄,可以修正这一问题,但是在 Iterator 没有结束时不能解决这个问题)。
4. 注意可变的 hashCode()
Map 是很好的集合,为我们带来了在其他语言(比如 Perl)中经常可见的好用的键/值对集合。JDK 以 HashMap 的形式为我们提供了方便的 Map 实现,它在内部使用哈希表实现了对键的对应值的快速查找。但是这里也有一个小问题:支持哈希码的键依赖于可变字段的内容,这样容易产生 bug,即使最耐心的 Java 开发人员也会被这些 bug 逼疯。
假设清单 3 中的 Person 对象有一个常见的 hashCode() (它使用 firstName、lastName 和 age 字段 — 所有字段都不是 final 字段 — 计算 hashCode()),对 Map 的 get() 调用会失败并返回 null:
清单 3. 可变 hashCode() 容易出现 bug
#div_code img{border:0px;} <!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->// Person.java import java.util.*; public class Person implements Iterable<Person> { public Person(String fn, String ln, int a, Person... kids) { this.firstName = fn; this.lastName = ln; this.age = a; for (Person kid : kids) children.add(kid); } // ... public void setFirstName(String value) { this.firstName = value; } public void setLastName(String value) { this.lastName = value; } public void setAge(int value) { this.age = value; } public int hashCode() { return firstName.hashCode() & lastName.hashCode() & age; } // ... private String firstName; private String lastName; private int age; private List<Person> children = new ArrayList<Person>(); } // MissingHash.java import java.util.*; public class MissingHash { public static void main(String[] args) { Person p1 = new Person("Ted", "Neward", 39); Person p2 = new Person("Charlotte", "Neward", 38); System.out.println(p1.hashCode()); Map<Person, Person> map = new HashMap<Person, Person>(); map.put(p1, p2); p1.setLastName("Finkelstein"); System.out.println(p1.hashCode()); System.out.println(map.get(p1)); } }
很显然,这种方法很糟糕,但是解决方法也很简单:永远不要将可变对象类型用作 HashMap 中的键。
5. equals() 与 Comparable
在浏览 Javadoc 时,Java 开发人员常常会遇到 SortedSet 类型(它在 JDK 中唯一的实现是 TreeSet)。因为 SortedSet 是 java.util 包中唯一提供某种排序行为的 Collection,所以开发人员通常直接使用它而不会仔细地研究它。清单 4 展示了:
清单 4. SortedSet,我很高兴找到了它!
#div_code img{border:0px;} <!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->import java.util.*; public class UsingSortedSet { public static void main(String[] args) { List<Person> persons = Arrays.asList( new Person("Ted", "Neward", 39), new Person("Ron", "Reynolds", 39), new Person("Charlotte", "Neward", 38), new Person("Matthew", "McCullough", 18) ); SortedSet ss = new TreeSet(new Comparator<Person>() { public int compare(Person lhs, Person rhs) { return lhs.getLastName().compareTo(rhs.getLastName()); } }); ss.addAll(perons); System.out.println(ss); } }
使用上述代码一段时间后,可能会发现这个 Set 的核心特性之一:它不允许重复。该特性在 Set Javadoc 中进行了介绍。Set 是不包含重复元素的集合。更准确地说,set 不包含成对的 e1 和 e2 元素,因此如果 e1.equals(e2),那么最多包含一个 null 元素。
但实际上似乎并非如此 — 尽管 清单 4 中没有相等的 Person 对象(根据 Person 的 equals() 实现),但在输出时只有三个对象出现在 TreeSet 中。
与 set 的有状态本质相反,TreeSet 要求对象直接实现 Comparable 或者在构造时传入 Comparator,它不使用 equals() 比较对象;它使用 Comparator/Comparable 的 compare 或 compareTo 方法。
因此存储在 Set 中的对象有两种方式确定相等性:大家常用的 equals() 方法和 Comparable/Comparator 方法,采用哪种方法取决于上下文。
更糟的是,简单的声明两者相等还不够,因为以排序为目的的比较不同于以相等性为目的的比较:可以想象一下按姓排序时两个 Person 相等,但是其内容却并不相同。
一定要明白 equals() 和 Comparable.compareTo() 两者之间的不同 — 实现 Set 时会返回 0。甚至在文档中也要明确两者的区别。
结束语
Java Collections 库中有很多有用之物,如果您能加以利用,它们可以让您的工作更轻松、更高效。但是发掘这些有用之物可能有点复杂,比如只要您不将可变对象类型作为键,您就可以用自己的方式使用 HashMap。
发表评论
-
Java编程中“为了性能”尽量要做到的一些地方
2012-09-14 10:33 12211. 尽量在合适的场合使 ... -
Java 5.0多线程编程
2012-08-17 19:16 1064概述 1:三个新加的多线程包 2:C ... -
Thread.setDaemon设置说明
2012-06-28 18:00 1152Thread.setDaemon的用法,经过学习以后了解: ... -
JVM运行时数据区
2012-03-07 13:15 1005JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些 ... -
使用ibatis防止sql注入
2011-12-19 19:59 4660为了防止SQL注入,iBatis模糊查询时也要避免使用$$来进 ... -
java的引用探讨
2011-12-18 16:03 1125Reference 是一个抽象类,而 SoftReferenc ... -
ClassLoader
2011-11-04 15:11 1194package org.liufei.neta.lib; ... -
仿照Hibernate实现一个SQLite的ORM框架
2011-09-30 20:06 3196那么先来看看使用 实体对象 package test; ... -
java压缩文件
2011-09-20 11:09 1295package org.liufei.net.util; ... -
获取客户机IP地址
2011-09-07 15:04 1033package org.liufei.jweb; imp ... -
Java读取本地机器MAC地址
2011-09-07 15:01 1299package org.liufei.jweb; imp ... -
获取IP地址
2011-09-07 13:41 2422public String getIpAddrByReques ... -
用CSS来美化Java桌面--Javacss
2011-09-02 12:14 1329CSS可以用来修饰HTML网页。 但你有没有想过,使用CSS ... -
java管理windows进程
2011-08-29 17:34 1780package org.zzuli.xmsb; /** ... -
java html工具
2011-08-29 17:26 1085package org.liufei.jweb.util; ... -
java将汉字转化为全拼
2011-08-29 17:24 1233package org.liufei.jweb.util; ... -
开源项目SVN源码地址
2011-08-22 16:20 3554多优秀的开源项目已经提供SVN源码签出了,无论是解疑还是学习, ... -
XML解析
2011-08-22 09:58 10221、DOM解析XML <?xml version=&q ... -
jdbc操作大观园
2011-08-09 17:22 1385最近公司使用jdbc和mybatis比较多,于是自己试着写了一 ... -
Windows XP系统总命令集合
2011-08-05 14:08 1076Windows XP系统总命令集合 winver----- ...
相关推荐
### 关于 Java Collections API 您不知道的 5 件事 #### 1. Collections 比数组更好 在 Java 的早期阶段,为了回应 C++ 开发者对于性能的批评,Java 引入了数组这一概念。然而,随着时间的发展,Java 的 ...
Java Collections API 是Java编程语言中不可或缺的一部分,它提供了丰富的接口和类来操作各种集合,如List、Set、Map等。这篇博客的第二部分将深入探讨关于Collections API的一些不那么为人所知的知识点,旨在帮助...
### 关于 Java Collections API 您不知道的 5 件事,第 1 部分 在 Java 开发领域,Collections API 已成为一种不可或缺的工具,它不仅简化了许多常见的编程任务,还提供了丰富的特性和灵活性。然而,对于许多开发者...
《Java Collections》是Apress出版社出版的一本专著,它深入浅出地讲解了Java Collections框架,这是一套用于管理对象集合的强大的API。这本书针对Java程序员,无论你是初学者还是经验丰富的开发者,都能从中...
根据提供的文件信息,我们可以推断出这是一本关于Java Collections的书籍,作者为John Zukowski。下面将基于这些信息来生成相关的Java Collections知识点。 ### Java Collections 概览 #### 一、简介 Java ...
Java API是Java编程语言的核心组成部分,它包含了各种预定义的类和接口,为开发者提供了丰富的功能,使得开发者能够构建复杂的软件系统。Java API_5.0中文版是Sun Microsystems(后被Oracle收购)官方发布的文档,是...
Java API 1.6 和 1.7 是Java开发的关键组成部分,它们包含了Java标准库的详细文档,这些库是编写Java应用程序和服务器端程序的基础。这两个版本在Java发展历程中占据了重要地位,引入了许多新特性,优化了性能,并...
10. **Stream API**: 自Java 8引入的Stream API为处理集合提供了新的方式,支持函数式编程风格,如过滤、映射、归约等操作。 了解和熟练使用Java集合框架是成为一名合格Java开发者的必要条件。通过深入学习这些知识...
这些API文档是开发者日常编程中不可或缺的参考资料,它们提供了详细的类库说明、方法解释以及示例代码,帮助开发者理解和使用Java平台的各种功能。 首先,JDBC(Java Database Connectivity)是Java访问数据库的...
Java 1.6 API 文档是 Java 开发者的重要参考资料,它包含了 Java 1.6 版本的所有公共类、接口、方法和异常等详细信息。这个可搜索的版本更便于开发者快速查找和理解相关功能,提高编程效率。在本文中,我们将深入...
Java 1.8 API官方文档是Java开发者不可或缺的参考资料,它详尽地介绍了Java开发平台的核心类库,涵盖了从基本数据类型、对象创建到高级特性如多线程、网络编程、I/O流、反射、泛型等各个方面。这份文档的中文版使得...
《Java JDK API 1.8谷歌翻译中文版在线参考手册》是Java开发者不可或缺的重要参考资料,它详尽地列出了Java开发工具包(JDK)1.8版本中的各种类库、接口、方法和异常,为程序员提供了清晰的API文档。这份手册通过...
Java API 6.0中文文档是Java开发者的重要参考资料,它详尽地介绍了Java SE(标准版)6.0中的各种类库、接口和方法。...因此,无论是初学者还是经验丰富的开发者,Java API 6.0中文文档都是不可或缺的工具。
Java 6.0 API中文文档是Java开发人员的重要参考资料,它包含了Java标准版6.0的所有公共类、接口和方法的详细说明。这份文档以CHM(Microsoft Compiled HTML Help)格式提供,便于在Windows XP及更高版本的操作系统上...
Java API(Application Programming Interface)是Java编程语言的核心组成部分,它为开发者提供了丰富的类库和接口,使得开发人员能够高效地构建各种类型的应用程序。在Java世界中,API涵盖了从基本的输入/输出到...
标签:eclipse、collections、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心...
Java通用API工具合集是Java开发者的重要参考资料,它包含了Java平台标准版(Java SE)的各种类库和接口的详尽文档。这个最全中文版使得中国开发者能够更方便地理解和使用Java API,避免了语言障碍,提高了开发效率。...