java容器类库的用途是保存对象,它提供两种不同的类型:
1、Collection。独立元素的序列。子类List必须按照插入的顺序保存元素;Set不能有重复的元素。
2、Map。一组成对的键值对对象。
在创建具体类的对象时,将其转型为对应的接口,然后在其余的代码中都使用这个接口,例如:
List<String> strList=new ArrayList<>();
List<String> strList2=new LinkedList<>();
但是LinkList具有在List中未包含的额外方法,如果想使用这些额外方法,那就不能将它们向上转型为更通用的接口。
实例,在Collection中填充Integer对象:
public static void main(String[] args) {
//继承自Collection的类的对象都可以使用
Collection<Integer> c=new ArrayList<Integer>();
for(int i=0;i<10;i++){
//在使用任何种类的List,不关心是否存在重复元素
//而在Set中,只有元素不存在才会被添加
c.add(i);
}
//遍历容器
for(Integer i:c){
System.out.println(i);
}
}
利用Arrays和Collection类中的实用方法,添加一组元素:
public static void main(String[] args) {
//Arrays.asList()方法可以接受一个数组或者一个逗号分隔的元素列表(可变参数,元素个数不固定)
Collection<Integer> collection=new ArrayList<Integer>(Arrays.asList(1,2,3));
Integer[] moreInt=new Integer[]{5,6,7};
//利用addAll()添加元素,只能接受另一个Collection类型
collection.addAll(Arrays.asList(moreInt));
for(Integer i:collection){
System.out.print(i+" ");
}
//注意,上面的collection只能接受Collection对象,这样不灵活
//可以使用Collections.addAll()方法,可以接受Collection对象,
//以及使用一个数组 或者 用逗号分隔的元素列表(可变参数)
//但是不能三者都放在一起,且必须有一个Collection对象,因为要将元素填充到collection中
Collections.addAll(collection,9,8);
Collections.addAll(collection,moreInt);
System.out.println();
for(Integer j:collection){
System.out.print(j+" ");
}
}
//结果:
1 2 3 5 6 7
1 2 3 5 6 7 9 8 5 6 7
当直接使用Arrays.asList()的输出,将其作为一个List,其底层表示的是数组,不能改变数组大小。
public static void main(String[] args) {
List<Integer> intList= Arrays.asList(3,2,4);
//增加或删除元素,会导致出现异常:UnsupportedOperationException
//intList.add(6);
//intList.add(4,6);
//intList.remove(0);
for(Integer i:intList){
System.out.println(i+" ");
}
}
Arrays.asList()方法会根据元素的实际类型产生正确的List类型:
public class AsListInference {
public static void main(String[] args) {
List<Snow> sonw= Arrays.asList(new Power(),new Crusty(),new Slush());
//不能编译通过
//List<Snow> snows_1=Arrays.asList(new Light(),new Heavy());
//混合情况也不能编译
//List<Snow> snow_2=Arrays.asList(new Power(),new Light());
//正确的方法,创建一个空的collection对象,在进行填充
List<Snow> snows_3=new ArrayList<Snow>();
Collections.addAll(snows_3,new Light(),new Heavy());
//也可以使用显式类型参数说明,告诉编译器List的实际类型,
List<Snow> snows_4=Arrays.<Snow>asList(new Light(),new Heavy());
}
}
class Snow{}
class Power extends Snow{}
class Light extends Power{}
class Heavy extends Power{}
class Crusty extends Snow{}
class Slush extends Snow{}
打印数组中的元素可以使用Array.toString()方法,但是容器可以直接打印:
public class PrintContain {
static Collection fill(Collection<String> collection){
collection.add("rat");
collection.add("cat");
collection.add("dog");
collection.add("dog");
return collection;
}
public static void main(String[] args) {
//打印数组
String[] str=new String[]{"rat","cat","dog","dog"};
System.out.println(Arrays.toString(str));
//打印容器
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>()));
}
}
//结果
//打印数组
[rat, cat, dog, dog]
//List类型按照插入的顺序保存元素
[rat, cat, dog, dog]
[rat, cat, dog, dog]
//Set类型,每个相同的项保存一次
//HashSet能最快获取元素,存储顺序无意义
[rat, cat, dog]
//TreeSet按照比较结果升序保存对象
[cat, dog, rat]
//LinkedHashSet按照被添加的顺序保存对象
[rat, cat, dog]
List容器
List可以将元素维护在特定的序列中,可以在List的中间插入和移除元素。
两种类型的List:
1、ArrayList:擅长随机访问元素,但在其中插入和移除元素时较慢
2、LinkedList:方便在List中间进行插入和删除操作,在随机访问方面比较慢。
迭代器
迭代器是一个轻量级对象,主要是遍历并选择序列中的对象,而不必关系该序列底层的结构。Java中的Iterator只能单向移动(也就是只能next,向前移动)。
public static void main(String[] args) {
List<String> strList=new ArrayList<String>();
Collections.addAll(strList,"dog","cat","mouse","rate");
//不必关心容器的确切类型
//方法iterator()返回一个Iterator,将返回序列的第一个元素
Iterator<String> iterator=strList.iterator();
//hasNext()检查序列中是否还有元素
while(iterator.hasNext()){
//获取序列中的下一个元素
String str=iterator.next();
System.out.print(str+" ");
}
System.out.println();
//因为iterator已经到最后没有元素了,需要重新建立对象
iterator=strList.iterator();
for(int i=0;i<2;i++){
//将新近返回的元素删除
iterator.next();
iterator.remove();
}
System.out.print(strList);
}
结果:
dog cat mouse rate
[mouse, rate]
ListIterator
它是Iterator的子类型,只能用于各种List类的访问。并且可以双向移动。
public static void main(String[] args) {
List<String> petList=new ArrayList<String>();
Collections.addAll(petList,"dog","cat","pig","rate");
ListIterator<String> li=petList.listIterator();
while (li.hasNext()){
//返回迭代器在列表中指向的当前位置的前一个和后一个元素的索引
String ne= li.next();
int next= li.nextIndex();
int pre= li.previousIndex();
System.out.print(ne+" "+next+" "+pre+" ");
}
System.out.println();
//也可以反向移动
while (li.hasPrevious()){
String previous= li.previous();
System.out.print(previous+" ");
}
System.out.println();
//可以一开始就指向列表索引为2的元素处的ListIterator
li=petList.listIterator(2);
while (li.hasNext()){
System.out.print(li.next()+" ");
}
}
//结果:
dog 1 0 cat 2 1 pig 3 2 rate 4 3
rate pig cat dog
pig rate
LinkedList
在List中插入和移除操作的效率比ArrayList更高效。但在随机访问方面要逊色一些。
public static void main(String[] args) {
List<String> link=new LinkedList<String>();
Collections.addAll(link,"dog","rat","mutt","pugl");
System.out.println("link="+link);
//返回列表的第一个元素
//list中没有addFirst()方法
System.out.println("link.get()="+link.get(0));
//移除并返回元素
System.out.println("link.remove()="+link.remove(1));
}
//结果
link=[dog, rat, mutt, pugl]
link.get()=dog
link.remove()=rat
Stack
栈指后进先出的容器,最后压入栈的元素,最后弹出栈。
LinkedList能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
public class StackTest<T> {
//Stack是用LinkedList实现的
private LinkedList<T> storge=new LinkedList<T>();
//插入一个元素
public void push(T t){storge.addFirst(t);}
//返回栈顶元素,但是并不移除
public T peek(){return storge.getFirst();}
//返回并移除栈顶元素
public T pop(){return storge.removeFirst();}
//判断栈是否为空
public boolean empty(){return storge.isEmpty();}
}
Set
set不保存重复对象。
存储方式的不同:HashSet使用的散列函数,输出的元素没有规律。
TreeSet将元素存储在红-黑树数据结构,对结果进行排序。
LinkedHashSet也使用散列函数,但是使用了链表来维护元素的插入顺序。
HashSet 和TreeSet
public static void main(String[] args) {
Random random=new Random(100);
Set<Integer> intset=new HashSet<Integer>();
Set<Integer> treeSet=new TreeSet<Integer>();
Collections.addAll(intset,21,32,4,6,1);
Collections.addAll(treeSet,21,32,4,6,1);
Iterator<Integer> it=intset.iterator();
while(it.hasNext()){
//遍历HashSet,输出的顺序没有规律可循
System.out.print(it.next()+" ");
}
System.out.println();
Iterator<Integer> itTree=treeSet.iterator();
while(itTree.hasNext()){
//遍历TreeSet,对结果进行排序
System.out.print(itTree.next()+" ");
}
}
//结果
32 1 4 21 6
1 4 6 21 32
Map
public static void main(String[] args) {
Map<String,String> petMap=new HashMap<String, String>();
petMap.put("my Cat","Molly");
petMap.put("my dog","ginger");
petMap.put("MY Mamster","Bosco");
//map中包含某个键或值
petMap.containsKey("my dog");
petMap.containsValue("my Cat");
//遍历map
//keySet返回键的Set
for(String my:petMap.keySet()){
System.out.println("主人:"+my+"宠物:"+petMap.get(my));
}
//values返回值的collection
Iterator<String> valIte=petMap.values().iterator();
while(valIte.hasNext()){
System.out.println(valIte.next());
}
}
Queue
队列是典型的先进先出的容器。从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列可以安全的将对象从一个任务传输给另一个任务。
LinkedList实现了Queue接口,可以把它作为一种实现。
public static void main(String[] args) {
Queue<Integer> queue=new LinkedList<Integer>();
Random random=new Random(47);
//offer方法将一个元素插入到队尾,返回true
System.out.println(queue.offer(444));
for(int i=0;i<10;i++){
queue.offer(random.nextInt(i+10));
}
System.out.println(queue);
}
Collection和Iterator
Collection是描述所有序列容器的共性的根接口,它会被认为是一个附属接口,为了表示其他接口的共性而出现的接口。AbstractCollection提供Collection的默认实现,在穿件AbstractCollection的子类型时,而其中没有不必要的代码的重复。
迭代器和容器都可以用在Map或Collection的子类型来工作。
//使用迭代器
public static void display(Iterator<String> it){
while (it.hasNext()){
System.out.print(it.next()+" ");
}
}
//使用容器
public static void display(Collection<String> c){
for(String str:c){
System.out.println(str+" ");
}
}
public static void main(String[] args) {
List<String> strList=new ArrayList<String>();
Set<String> strSet=new HashSet<String>();
Collections.addAll(strList,"dog","pig","cat");
Collections.addAll(strSet,"first","second","third");
Map<String,String> strMap=new LinkedHashMap<String, String>();
strMap.put("zhangsan","lisi");
strMap.put("wangwu","zhaoliu");
//使用迭代器
display(strList.iterator());
display(strSet.iterator());
display(strMap.keySet().iterator());
//使用容器
display(strList);
display(strSet);
display(strMap.values());
}
当要实现一个不是Collection的外部类时,让其实现该接口会非常困难,即使通过继承AbstractCollection而很容易实现,但是还是需要强制实现iterator()和size()方法。此时使用迭代器比较方便。
Foreach与迭代器
foreach语法主要用于数组,但是也可以应用于任何Collection对象。所有的Collection类(不包括各种Map)都是Iterable类型,数组也不是Iterable类型。Iterable接口包含一个能够产生Iterator的iterator()方法。并且Iterable接口被foreach用来在序列中移动。
任何实现了Iterable的类,都可以将它应用于foreach中。
public static void main(String[] args) {
Map<String,String> strMap=new HashMap<String, String>();
strMap.put("one","zhangsan");
strMap.put("two","lisi");
strMap.put("three","wangwu");
for(Map.Entry entry:strMap.entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
//结果
one:zhangsan
two:lisi
three:wangwu