Java中java中的collections和java中的collectionss的区别

&&&&Collection与Collections,Array与Arrays的区别
Collection与Collections,Array与Arrays的区别
本文详细讲解了java中Collection与Collections,Array与Arrays的区别。
嵌到我的页面
<input type="text" readonly="true" value="">
若举报审核通过,可奖励20下载分
被举报人:
richardtxws
举报的资源分:
请选择类型
资源无法下载
资源无法使用
标题与实际内容不符
含有危害国家安全内容
含有反动色情等内容
含广告内容
版权问题,侵犯个人或公司的版权
*详细原因:
您可能还需要
开发技术下载排行java中ArrayList 、LinkList的区别分析
字体:[ ] 类型:转载 时间:
java中ArrayList 、LinkList的区别分析,需要的朋友可以参考一下
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 &&&& 2.对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,而LinkedList要移动指针一步一步的移动到节点处。(参考数组与链表来思考)&&&& 3.对于新增和删除操作add和remove,LinedList比较占优势,只需要对指针进行修改即可,而ArrayList要移动数据来填补被删除的对象的空间。
ArrayList和LinkedList是两个集合类,用于存储一系列的对象引用(references)。例如我们可以用ArrayList来存储一系列的String或者Integer。那么ArrayList和LinkedList在性能上有什么差别呢?什么时候应该用ArrayList什么时候又该用LinkedList呢?
一.时间复杂度
首先一点关键的是,ArrayList的内部实现是基于基础的对象数组的,因此,它使用get方法访问列表中的任意一个元素时(random-access),它的速度要比LinkedList快。LinkedList中的get方法是按照顺序从列表的一端开始检查,直到另外一端。对LinkedList而言,访问列表中的某个指定元素没有更快的方法了。
假设我们有一个很大的列表,它里面的元素已经排好序了,这个列表可能是ArrayList类型的也可能是LinkedList类型的,现在我们对这个列表来进行二分查找(binary search),比较列表是ArrayList和LinkedList时的查询速度,看下面的程序:
代码如下:package com.mangocity. import java.util.LinkedL import java.util.L import java.util.R import java.util.ArrayL import java.util.A import java.util.C public class TestList ...{ &&&& public static final int N=50000;&&&& public static L&&&& static...{ &&&&&&&& Integer vals[]=new Integer[N];&&&&&&&& Random r=new Random();&&&&&&&& for(int i=0,currval=0;i&N;i++)...{ &&&&&&&&&&&& vals=new Integer(currval); &&&&&&&&&&&& currval+=r.nextInt(100)+1; &&&&&&&& }&&&&&&&& values=Arrays.asList(vals); &&&& }&&&& static long timeList(List lst)...{ &&&&&&&& long start=System.currentTimeMillis(); &&&&&&&& for(int i=0;i&N;i++)...{ &&&&&&&&&&&& int index=Collections.binarySearch(lst, values.get(i)); &&&&&&&&&&&& if(index!=i) &&&&&&&&&&&&&&&& System.out.println("***错误***"); &&&&&&&& } &&&&&&&& return System.currentTimeMillis()- &&&& } &&&& public static void main(String args[])...{ &&&&&&&& System.out.println("ArrayList消耗时间:"+timeList(new ArrayList(values))); &&&&&&&& System.out.println("LinkedList消耗时间:"+timeList(new LinkedList(values))); &&&& } }&我得到的输出是:ArrayList消耗时间:15
&&&&&&&&&&&&&&&& LinkedList消耗时间:2596
这个结果不是固定的,但是基本上ArrayList的时间要明显小于LinkedList的时间。因此在这种情况下不宜用LinkedList。二分查找法使用的随机访问(randomaccess)策略,而LinkedList是不支持快速的随机访问的。对一个LinkedList做随机访问所消耗的时间与这个list的大小是成比例的。而相应的,在ArrayList中进行随机访问所消耗的时间是固定的。
这是否表明ArrayList总是比LinkedList性能要好呢?这并不一定,在某些情况下LinkedList的表现要优于ArrayList,有些算法在LinkedList中实现时效率更高。比方说,利用Collections.reverse方法对列表进行反转时,其性能就要好些。
看这样一个例子,假如我们有一个列表,要对其进行大量的插入和删除操作,在这种情况下LinkedList就是一个较好的选择。请看如下一个极端的例子,我们重复的在一个列表的开端插入一个元素:
代码如下:package com.mangocity.import java.util.*; public class ListDemo { &&&& static final int N=50000; &&&& static long timeList(List list){ &&&& long start=System.currentTimeMillis(); &&&& Object o = new Object(); &&&& for(int i=0;i&N;i++) &&&&&&&& list.add(0, o); &&&& return System.currentTimeMillis()- &&&& } &&&& public static void main(String[] args) { &&&&&&&& System.out.println("ArrayList耗时:"+timeList(new ArrayList())); &&&&&&&& System.out.println("LinkedList耗时:"+timeList(new LinkedList())); &&&& } }
& 这时我的输出结果是:ArrayList耗时:2463
&&&&&&&&&&&&&&&&&&&&&&&&&& LinkedList耗时:15
这和前面一个例子的结果截然相反,当一个元素被加到ArrayList的最开端时,所有已经存在的元素都会后移,这就意味着数据移动和复制上的开销。相反的,将一个元素加到LinkedList的最开端只是简单的为这个元素分配一个记录,然后调整两个连接。在LinkedList的开端增加一个元素的开销是固定的,而在ArrayList的开端增加一个元素的开销是与ArrayList的大小成比例的。
二.空间复杂度
在LinkedList中有一个私有的内部类,定义如下:
private static class Entry {&&&&&&&& O &&&&&&&& E &&&&&&&& E &&&& }
每个Entry对象reference列表中的一个元素,同时还有在LinkedList中它的上一个元素和下一个元素。一个有1000个元素的LinkedList对象将有1000个链接在一起的Entry对象,每个对象都对应于列表中的一个元素。这样的话,在一个LinkedList结构中将有一个很大的空间开销,因为它要存储这1000个Entity对象的相关信息。
ArrayList使用一个内置的数组来存储元素,这个数组的起始容量是10.当数组需要增长时,新的容量按如下公式获得:新容量=(旧容量*3)/2+1,也就是说每一次容量大概会增长50%。这就意味着,如果你有一个包含大量元素的ArrayList对象,那么最终将有很大的空间会被浪费掉,这个浪费是由ArrayList的工作方式本身造成的。如果没有足够的空间来存放新的元素,数组将不得不被重新进行分配以便能够增加新的元素。对数组进行重新分配,将会导致性能急剧下降。如果我们知道一个ArrayList将会有多少个元素,我们可以通过构造方法来指定容量。我们还可以通过trimToSize方法在ArrayList分配完毕之后去掉浪费掉的空间。
ArrayList和LinkedList在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下:
性能总结:
delete()操作
insert操作
index取值操作
iterator取值操作
ArrayList/Vector/Stack
LinkedList
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
3.LinkedList不支持高效的随机元素访问。
4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
可以这样说:当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
java中ArrayList 、List区别
List集合&&& List继承自Collection接口。List是一种有序集合,List中的元素可以根据索引(顺序号:元素在集合中处于的位置信息)进行取得/删除/插入操作。
&&& 跟Set集合不同的是,List允许有重复元素。对于满足e1.equals(e2)条件的e1与e2对象元素,可以同时存在于List集合中。当然,也有List的实现类不允许重复元素的存在。&& 同时,List还提供一个listIterator()方法,返回一个ListIterator接口对象,和Iterator接口相比,ListIterator添加元素的添加,删除,和设定等方法,还能向前或向后遍历。
List跟Collection的关系:java.util.Collection [I]+--java.util.List [I]&& +--java.util.ArrayList [C]&& +--java.util.LinkedList [C]&& +--java.util.Vector [C]&&&&& +--java.util.Stack [C]
List接口的实现类主要有ArrayList,LinkedList,Vector,Stack等。
父子关系.&& List是一个接口,ArrayList继承与这个接口并实现了它.&& 用的时候一般都用ArrayList.没用过List. 可以这么用:List list = new ArrayList();
Collection接口&&& Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,JavaSDK提供的类都是继承自Collection的“子接口”如List和Set。&&& 所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
&&&& 如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:&&& Iterator it = collection.iterator(); // 获得一个迭代子&&& while(it.hasNext()) {&&&&&&&&&&&&&&&&&&&&&&&&&&&& Object obj = it.next(); // 得到下一个元素&&&&&& }由Collection接口派生的两个接口是List和Set。
&&& List接口:&&& List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素。除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。&&& 实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
&&&& LinkedList类&&&& LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。&&& 注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
总结  如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。&&&&& 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具Java中Collection和Collections的区别_百度知道
Java中Collection和Collections的区别
提问者采纳
ArrayList,里面有好多对集合操作的静态方法, TreeSet, HashSet:Collection是一个集合的父接口:Collections是集合的工具类,使用很方便总的来说Collection是一个接口1, Vector是它的实现类2
研发工程师
其他类似问题
为您推荐:
java的相关知识
其他1条回答
Collection是集合框架中的其中一种,Collections是集合的工具类提供了操作集合的静态方法
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
您可以在任何地方使用 Java&#8482; 集合,但是一定要小心。集合有很多秘密,如果不正确处理可能会带来麻烦。Ted 探索了 Java Collections API 复杂、多变的一面并为您提供了一些技巧,帮您充分利用 Iterable、HashMap 和 SortedSet,又不会带来 bug。
, 总裁,ThoughtWorks, ThoughtWorks
Ted Neward 是一家全球咨询公司 ThoughtWorks 的顾问,也是 Neward & Associates 的总裁,从事关于 Java、.NET、XML Services 以及其他平台方面的咨询、指导和演示等工作。他居住在华盛顿西雅图。
java.util 中的 Collections 类旨在通过取代数组提高 Java 性能。如您在
中了解到的,它们也是多变的,能够以各种方式定制和扩展,帮助实现优质、简洁的代码。Collections 非常强大,但是很多变:使用它们要小心,滥用它们会带来风险。1. List 不同于数组Java 开发人员常常错误地认为 ArrayList 就是 Java 数组的替代品。Collections 由数组支持,在集合内随机查找内容时性能较好。与数组一样,集合使用整序数获取特定项。但集合不是数组的简单替代。要明白数组与集合的区别需要弄清楚顺序 和位置 的不同。例如,List 是一个接口,它保存各个项被放入集合中的顺序,如清单 1 所示:清单 1. 可变键值import java.util.*;
public class OrderAndPosition
public static &T& void dumpArray(T[] array)
System.out.println("=============");
for (int i=0; i&array. 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);
dumpArray(args);
dumpList(argList);
argList.remove(1);
dumpList(argList);
}当第三个元素从上面的 List 中被移除时,其 “后面” 的各项会上升填补空位。很显然,此集合行为与数组的行为不同(事实上,从数组中移除项与从 List 中移除它也不完全是一回事儿 &#8212; 从数组中 “移除” 项意味着要用新引用或 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. 迭代文件// 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 !=
public String next() {
String retval =
line = getLine();
public void remove() {
throw new UnsupportedOperationException();
String getLine() {
String line =
line = br.readLine();
catch (IOException ioEx) {
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 字段 &#8212; 所有字段都不是 final 字段 &#8212; 计算 hashCode()),对 Map 的 get() 调用会失败并返回 null:清单 3. 可变 hashCode() 容易出现 bug// Person.java
import java.util.*;
public class Person
implements Iterable&Person&
public Person(String fn, String ln, int a, Person... kids)
this.firstName = this.lastName = this.age =
for (Person kid : kids)
children.add(kid);
public void setFirstName(String value) { this.firstName = }
public void setLastName(String value) { this.lastName = }
public void setAge(int value) { this.age = }
public int hashCode() {
return firstName.hashCode() & lastName.hashCode() &
private String firstN
private String lastN
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,我很高兴找到了它!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 元素。 但实际上似乎并非如此 &#8212; 尽管
中没有相等的 Person 对象(根据 Person 的 equals() 实现),但在输出时只有三个对象出现在 TreeSet 中。与 set 的有状态本质相反,TreeSet 要求对象直接实现 Comparable 或者在构造时传入 Comparator,它不使用 equals() 比较对象;它使用 Comparator/Comparable 的 compare 或 compareTo 方法。 因此存储在 Set 中的对象有两种方式确定相等性:大家常用的 equals() 方法和 Comparable/Comparator 方法,采用哪种方法取决于上下文。更糟的是,简单的声明两者相等还不够,因为以排序为目的的比较不同于以相等性为目的的比较:可以想象一下按姓排序时两个 Person 相等,但是其内容却并不相同。一定要明白 equals() 和 <pareTo() 两者之间的不同 &#8212; 实现 Set 时会返回 0。甚至在文档中也要明确两者的区别。结束语Java Collections 库中有很多有用之物,如果您能加以利用,它们可以让您的工作更轻松、更高效。但是发掘这些有用之物可能有点复杂,比如只要您不将可变对象类型作为键,您就可以用自己的方式使用 HashMap。至此我们挖掘了 Collections 的一些有用特性,但我们还没有挖到金矿:Concurrent Collections,它在 Java 5 中引入。本
的后 5 个窍门将关注 java.util.concurrent。
下载描述名字大小位置应用程序的源代码15KB
参考资料 “”(Ted Neward,developerWorks,2010 年 4 月):用 Java Collections 做更多事情,包括关于定制和扩展 Java Collections API。“”(MageLang Institute,Sun Developer Network,1999):这篇教程是很早以前的,但是很棒,它对并发集合之前的 Java Collections Framework 做了完整的介绍。“”(Brian Goetz,developerWorks,2003 年 7 月):学习 Doug Lea 的 util.concurrent 包如何为标准集合类型 List 和 Map 注入新的活力。“”(John Zukowski,developerWorks,2008 年 4 月):介绍 Java 6 中 Java Collections Framework 的变化。:阅读 Sun Microsystems 的 Java Collections Framework 和 API 文档。:这里有数百篇关于 Java 编程每个方面的文章。加入 。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Java technologyArticleID=496862ArticleTitle=关于 Java Collections API 您不知道的 5 件事,第 2 部分publish-date=

我要回帖

更多关于 java collections api 的文章

 

随机推荐