Java Collections API 您不知道的 5 件事.docx
- 文档编号:10845960
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:26
- 大小:26.14KB
Java Collections API 您不知道的 5 件事.docx
《Java Collections API 您不知道的 5 件事.docx》由会员分享,可在线阅读,更多相关《Java Collections API 您不知道的 5 件事.docx(26页珍藏版)》请在冰豆网上搜索。
JavaCollectionsAPI您不知道的5件事
对于很多Java开发人员来说,JavaCollectionsAPI是标准Java数组及其所有缺点的一个非常需要的替代品。
将Collections主要与ArrayList 联系到一起本身没有错,但是对于那些有探索精神的人来说,这只是Collections的冰山一角。
虽然 Map(以及它的常用实现 HashMap)非常适合名-值对或键-值对,但是没有理由让自己局限于这些熟悉的工具。
可以使用适当的API,甚至适当的Collection来修正很多易错的代码。
之所以讨论Collections,是因为这些集合在Java编程中是如此重要。
首先我将讨论做每件事的最快(但也许不是最常见)的方式,例如将 Array 中的内容转移到 List。
然后我们深入探讨一些较少人知道的东西,例如编写定制的Collections类和扩展JavaCollectionsAPI。
1.Collections比数组好
刚接触Java技术的开发人员可能不知道,Java语言最初包括数组,是为了应对上世纪90年代初期C++开发人员对于性能方面的批评。
从那时到现在,我们已经走过一段很长的路,如今,与JavaCollections库相比,数组不再有性能优势。
例如,若要将数组的内容转储到一个字符串,需要迭代整个数组,然后将内容连接成一个 String;而Collections的实现都有一个可用的 toString() 实现。
除少数情况外,好的做法是尽快将遇到的任何数组转换成集合。
于是问题来了,完成这种转换的最容易的方式是什么?
事实证明,JavaCollectionsAPI使这种转换变得容易,如清单1所示:
清单1.ArrayToList
importjava.util.*;
publicclassArrayToList
{
publicstaticvoidmain(String[]args)
{
//Thisgivesusnothinggood
System.out.println(args);
//ConvertargstoaListofString
List
//Printthemout
System.out.println(argList);
}
}
注意,返回的 List 是不可修改的,所以如果尝试向其中添加新元素将抛出一个 UnsupportedOperationException。
而且,由于 Arrays.asList() 使用 varargs 参数表示添加到 List 的元素,所以还可以使用它轻松地用以 new 新建的对象创建 List。
2.迭代的效率较低
将一个集合(特别是由数组转化而成的集合)的内容转移到另一个集合,或者从一个较大对象集合中移除一个较小对象集合,这些事情并不鲜见。
您也许很想对集合进行迭代,然后添加元素或移除找到的元素,但是不要这样做。
在此情况下,迭代有很大的缺点:
∙每次添加或移除元素后重新调整集合将非常低效。
∙每次在获取锁、执行操作和释放锁的过程中,都存在潜在的并发困境。
∙当添加或移除元素时,存取集合的其他线程会引起竞争条件。
可以通过使用 addAll 或 removeAll,传入包含要对其添加或移除元素的集合作为参数,来避免所有这些问题。
3.用for循环遍历任何Iterable
Java5中加入Java语言的最大的便利功能之一,增强的for循环,消除了使用Java集合的最后一道障碍。
以前,开发人员必须手动获得一个 Iterator,使用 next() 获得 Iterator 指向的对象,并通过 hasNext() 检查是否还有更多可用对象。
从Java5开始,我们可以随意使用for循环的变种,它可以在幕后处理上述所有工作。
实际上,这个增强适用于实现 Iterable 接口的任何对象,而不仅仅是 Collections。
清单2显示通过 Iterator 提供 Person 对象的孩子列表的一种方法。
这里不是提供内部 List 的一个引用(这使 Person 外的调用者可以为家庭增加孩子—而大多数父母并不希望如此),Person 类型实现 Iterable。
这种方法还使得for循环可以遍历所有孩子。
清单2.增强的for循环:
显示孩子
//Person.java
importjava.util.*;
publicclassPerson
implementsIterable
{
publicPerson(Stringfn,Stringln,inta,Person...kids)
{
this.firstName=fn;this.lastName=ln;this.age=a;
for(Personchild:
kids)
children.add(child);
}
publicStringgetFirstName(){returnthis.firstName;}
publicStringgetLastName(){returnthis.lastName;}
publicintgetAge(){returnthis.age;}
publicIterator
publicvoidsetFirstName(Stringvalue){this.firstName=value;}
publicvoidsetLastName(Stringvalue){this.lastName=value;}
publicvoidsetAge(intvalue){this.age=value;}
publicStringtoString(){
return"[Person:
"+
"firstName="+firstName+""+
"lastName="+lastName+""+
"age="+age+"]";
}
privateStringfirstName;
privateStringlastName;
privateintage;
privateList
}
//App.java
publicclassApp
{
publicstaticvoidmain(String[]args)
{
Personted=newPerson("Ted","Neward",39,
newPerson("Michael","Neward",16),
newPerson("Matthew","Neward",10));
//Iterateoverthekids
for(Personkid:
ted)
{
System.out.println(kid.getFirstName());
}
}
}
在域建模的时候,使用 Iterable 有一些明显的缺陷,因为通过 iterator() 方法只能那么“隐晦”地支持一个那样的对象集合。
但是,如果孩子集合比较明显,Iterable 可以使针对域类型的编程更容易,更直观。
4.经典算法和定制算法
您是否曾想过以倒序遍历一个 Collection?
对于这种情况,使用经典的JavaCollections算法非常方便。
在上面的 清单2 中,Person 的孩子是按照传入的顺序排列的;但是,现在要以相反的顺序列出他们。
虽然可以编写另一个for循环,按相反顺序将每个对象插入到一个新的 ArrayList 中,但是3、4次重复这样做之后,就会觉得很麻烦。
在此情况下,清单3中的算法就有了用武之地:
清单3.ReverseIterator
publicclassReverseIterator
{
publicstaticvoidmain(String[]args)
{
Personted=newPerson("Ted","Neward",39,
newPerson("Michael","Neward",16),
newPerson("Matthew","Neward",10));
//MakeacopyoftheList
List
//Reverseit
Collections.reverse(kids);
//Displayit
System.out.println(kids);
}
}
Collections 类有很多这样的“算法”,它们被实现为静态方法,以 Collections 作为参数,提供独立于实现的针对整个集合的行为。
而且,由于很棒的API设计,我们不必完全受限于 Collections 类中提供的算法—例如,我喜欢不直接修改(传入的Collection的)内容的方法。
所以,可以编写定制算法是一件很棒的事情,例如清单4就是一个这样的例子:
清单4.ReverseIterator使事情更简单
classMyCollections
{
publicstatic
{
List
Collections.reverse(results);
returnresults;
}
}
5.扩展CollectionsAPI
以上定制算法阐释了关于JavaCollectionsAPI的一个最终观点:
它总是适合加以扩展和修改,以满足开发人员的特定目的。
例如,假设您需要 Person 类中的孩子总是按年龄排序。
虽然可以编写代码一遍又一遍地对孩子排序(也许是使用 Collections.sort方法),但是通过一个 Collection 类来自动排序要好得多。
实际上,您甚至可能不关心是否每次按固定的顺序将对象插入到 Collection 中(这正是 List 的基本原理)。
您可能只是想让它们按一定的顺序排列。
java.util 中没有 Collection 类能满足这些需求,但是编写一个这样的类很简单。
只需创建一个接口,用它描述 Collection 应该提供的抽象行为。
对于 SortedCollection,它的作用完全是行为方面的。
清单5.SortedCollection
publicinterfaceSortedCollection
{
publicComparator
publicvoidsetComparator(Comparator
}
编写这个新接口的实现简直不值一提:
清单6.ArraySortedCollection
importjava.util.*;
publicclassArraySortedCollection
implementsSortedCollection
{
privateComparator
privateArrayList
publicArraySortedCollection(Comparator
{
this.list=newArrayList
parator=c;
}
publicArraySortedCollection(Collection
extendsE>src,Comparator
{
this.list=newArrayList
parator=c;
sortThis();
}
publicComparator
publicvoidsetComparator(Comparator
publicbooleanadd(Ee)
{booleanr=list.add(e);sortThis();returnr;}
publicbooleanaddAll(Collection
extendsE>ec)
{booleanr=list.addAll(ec);sortThis();returnr;}
publicbooleanremove(Objecto)
{booleanr=list.remove(o);sortThis();returnr;}
publicbooleanremoveAll(Collection
>c)
{booleanr=list.removeAll(c);sortThis();returnr;}
publicbooleanretainAll(Collection
>ec)
{booleanr=list.retainAll(ec);sortThis();returnr;}
publicvoidclear(){list.clear();}
publicbooleancontains(Objecto){returnlist.contains(o);}
publicbooleancontainsAll(Collection
>c){returnlist.containsAll(c);}
publicbooleanisEmpty(){returnlist.isEmpty();}
publicIterator
publicintsize(){returnlist.size();}
publicObject[]toArray(){returnlist.toArray();}
public
publicbooleanequals(Objecto)
{
if(o==this)
returntrue;
if(oinstanceofArraySortedCollection)
{
ArraySortedCollection
returnthis.list.equals(rhs.list);
}
returnfalse;
}
publicinthashCode()
{
returnlist.hashCode();
}
publicStringtoString()
{
returnlist.toString();
}
privatevoidsortThis()
{
Collections.sort(list,comparator);
}
}
这个实现非常简陋,编写时并没有考虑优化,显然还需要进行重构。
但关键是JavaCollectionsAPI从来无意将与集合相关的任何东西定死。
它总是需要扩展,同时也鼓励扩展。
当然,有些扩展比较复杂,例如 java.util.concurrent 中引入的扩展。
但是另一些则非常简单,只需编写一个定制算法,或者已有Collection 类的简单的扩展。
扩展JavaCollectionsAPI看上去很难,但是一旦开始着手,您会发现远不如想象的那样难。
java.util 中的Collections类旨在通过取代数组提高Java性能。
从之前了解到的,它们也是多变的,能够以各种方式定制和扩展,帮助实现优质、简洁的代码。
Collections非常强大,但是很多变:
使用它们要小心,滥用它们会带来风险。
6.List不同于数组
Java开发人员常常错误地认为 ArrayList 就是Java数组的替代品。
Collections由数组支持,在集合内随机查找内容时性能较好。
与数组一样,集合使用整序数获取特定项。
但集合不是数组的简单替代。
要明白数组与集合的区别需要弄清楚顺序 和位置 的不同。
例如,List 是一个接口,它保存各个项被放入集合中的顺序,如清单1所示:
清单1.可变键值
importjava.util.*;
publicclassOrderAndPosition
{
publicstatic
{
System.out.println("=============");
for(inti=0;i System.out.println("Position"+i+": "+array[i]); } publicstatic { System.out.println("============="); for(inti=0;i System.out.println("Ordinal"+i+": "+list.get(i)); } publicstaticvoidmain(String[]args) { List dumpArray(args); args[1]=null; dumpArray(args); dumpList(argList); argList.remove (1); dumpList(argList); } } 当第三个元素从上面的 List 中被移除时,其“后面”的各项会上升填补空位。 很显然,此集合行为与数组的行为不同(事实上,从数组中移除项与从 List 中移除它也不完全是一回事儿—从数组中“移除”项意味着要用新引用或null覆盖其索引槽)。 7.令人惊讶的 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 容易得多。 8.并非所有 Iterable 都来自集合 Ruby和Groovy开发人员喜欢炫耀他们如何能迭代整个文本文件并通过一行代码将其内容输出到控制台。 通常,他们会说在Java编程中完成同样的操作需要很多行代码: 打开 FileReader,然后打开 BufferedReader,接着创建 while() 循环来调用 getLine(),直到它返回 null。 当然,在 try/catch/finally 块中必须要完成这些操作,它要处理异常并在结束时关闭文件句柄。 这看起来像是一个没有意义的学术上的争论,但是它也有其自身的价值。 他们(包括相当一部分Java开发人员)不知道并不是所有 Iterable 都来自集合。 Iterable 可以创建 Iterator,该迭代器知道如何凭空制造下一个元素,而不是从预先存在的 Collection 中盲目地处理: 清单2.迭代文件 //FileUtils.java importjava.io.*; importjava.util.*; publicclassFileUtils { publicstaticIterable throwsIOException { finalFileReaderfr=newFileReader(filename); finalBufferedReaderbr=newBufferedReader(fr); returnnewIterable public returnnew publicbooleanhasNext(){ retIterator
Iterator
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java Collections API 您不知道的 件事 知道
![提示](https://static.bdocx.com/images/bang_tan.gif)