[关闭]
@w460461339 2016-11-16T07:03:07.000000Z 字数 6356 阅读 863

Java学习Day4(Collection List 迭代器)

Java基础


今天主要讲的是集合。集合中有很多,比如之前学过的ArrayList,HashMap,HashSet等等,今天主要梳理一下它们之间的关系以及他们之中的方法。从图中可以看到,所有集合的根父类是Collection,它是一个接口,List和Set也都是接口,因此,要实例化它们,需要用它们的子类,诸如ArrayList,HashSet等来创建(其实真实的情况比这个复杂很多,但大体是这么个意思)。学习这样的继承链,我们一般从顶层父类学起。所以此处我们从Collection类学起。
Java 集合关系图

1、Collection类

1.1Collection类实现

由于Collection是一个接口,所有需要一个子类去实例化它。它的直接子类List和Set都是接口,只能由再下一层的子类实例化。这里我们选择ArrayList类。

  1. public static void main(String args[]){
  2. Collection c=new ArrayList();//导包可以用Ctrl+shift+o来导包。
  3. }
1.2Collection所包含的方法

所包含方法如下:

boolean add();void clear();boolean remove(); boolean isEmpty()
boolean addAll(); boolean removeAll(); boolean containAll();
boolean retainAll(); Object[] toArray();Iterator<E> iterator()…… 

其中的方法体和参数列表全部略去了,到时候直接查API就好。注意其中涉及到对集合进行修改的方法,是哪个对象调用,就对哪个对象进行修改。其实因为这是借口,所以查它的API也没太大意思,更多的还是需要去实现它的子类中去查看是如何实现这些方法的。

大多数方法通过名字就可以明了,这里说三个方法:

1.2.1 boolean retainAll(Collection c)

它的意思是取交集,返回值是boolean类型,即原集合的元素更改了,返回true,否则返回false。

  1. public static void main(String[] args) {
  2. Collection c=new ArrayList();
  3. Collection d=new ArrayList();
  4. c.add("Hello");
  5. d.add("Hello");
  6. d.add("World");
  7. System.out.println(c.retainAll(d));//返回false,c中的元素没变
  8. System.out.println(c);//打印Hello
  9. System.out.println(d.retainAll(c));//返回true,d中元素更改了
  10. System.out.println(d);//打印Hello
  11. }
1.2.2 Object[] toArray()

它是将集合中的元素转成数组然后返回。这样一来我们可以利用数组的一些特性,比如下标索引,遍历等方法来对其进行操作。

  1. public static void main(String[] args) {
  2. Collection d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. Object[] objd=d.toArray();//Object类型数组,每个格子是Object类型的引用,指向一个String类型对象
  7. for(int i=0;i<objd.length;i++){
  8. System.out.println(objd[i]);
  9. //由于引用的类型(静态类型)中的toString方法被实际类型(动态类型)的变量覆盖,所以这里调用String类型中toString方法,打印String类型对象。
  10. }
  11. }
1.2.3 Iterator iterator()

这货是返回一个迭代器类型的对象,所以我们需要由一个迭代器类型的引用来接收它:

  1. public static void main(String[] args) {
  2. Collection d=new ArrayList();
  3. d.add("Hello");
  4. d.add("World");
  5. Iterator it=d.iterator();
  6. }

然而Iterator也是个接口,那么我们就知道,iterator()这个方法,返回的必然是一个可以实例化的Iterator的子类对象。这里我们可以进ArrayList里面看看,发现这里面没有,原来它是在它的抽象父类,AbstractList中,具体描述是这样的:

  1. public Iterator<E> iterator() {
  2. return new Itr();//这货是个内部类,它实现了那个Iterator接口,这里不看了,想看自己去查API。
  3. }

所以,这里我们可以理一下迭代器和集合之间的关系。最先放出来的那张图,就是集合的接口以及子类之间的关系。迭代器的接口以及其子类,也是那样的关系。那是什么将他们连起来的呢?由于迭代器需要依赖于集合存在,单纯定义一个迭代器是没有意义的,所以只能在实例化集合对象之后才能创建迭代器。因此,将迭代器定义为集合类的子类,并且外部不能访问这个类,只能通过它提供的方法来获得一个迭代器的对象。(喵,就是这样)

讲完了大致的原理,我们来看看迭代器的用法(也就是看看方法)。同样,我们从顶向下看,先看看顶层类。顶层类是Iterator,它是一个接口,提供三个方法:next,hasNext 以及remove(当然,这些都是在具体子类中被各自实现了,所以各个子类中有一些细微差异)。

  1. public static void main(String[] args) {
  2. Collection d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. Iterator it=d.iterator();
  7. //当然,你用for循环也没问题= -
  8. while(it.hasNext()){//判断是否还有元素,没了就跳出
  9. //这里向下造型,当然你直接打印it.next()也没问题,
  10. //不过万一会用到String类型中的某些方法,这样就比较方便了.
  11. //从第一个元素开始拿,拿完之后自动指向下一个
  12. String s = (String)it.next();
  13. System.out.println(s);
  14. }
  15. it.remove();//移除迭代器曾经指向的最后一个元素。
  16. System.out.println(d);
  17. }

2、List类

List和Collection一样,也是个接口。但不同的是之前的Collection可以有序储存,也可以无序储存不同,List类是仅有序存储的。有序的意思是和输入的顺序和打印的顺序一样,而不是指排序哦。List类和数组很像,很多操作可以通过整数下标来进行索引。

2.1 List类所包含方法

List的实例化就不说了,和Collection基本一样。由于是个接口,所以它从父类Collection中继承了它所有的方法,并且没有实现。除此之外,它还利用它的类数组特性,对某些函数进行了重载以及增加了新的函数。

2.1.1 void add(int index,Element element)

这个add函数表示在指定位置index插入同集合中元素相同类型Element的对象element。注意,若原集合中有三个元素,其索引为0,1,2;则插入的指定位置包括集合中的这三个位置,以及之后的一个位置,即4.

  1. public static void main(String args[]){
  2. List list=new ArrayList();
  3. list.add("Hello");
  4. list.add("world");
  5. list.add(1,"the");//储存顺序为:hello the world
  6. list.add(3,"java");//储存顺序为:hello the world java
  7. }
2.1.2 Element get(int index)以及 Element set(int index Element element)

这个很好理解,get是从指定位置拿回元素,set是修改指定位置的元素,只不过会把之前的元素返回。

  1. public static void main(String args[]){
  2. List list=new ArrayList();
  3. list.add("Hello");
  4. list.add("world");
  5. list.add(1,"the");//储存顺序为:hello the world
  6. list.add(3,"java");//储存顺序为:hello the world java
  7. list.get(3)//拿到java
  8. list.set(3,"javaee")//返回java,储存顺序:hello the world javaee
  9. }
2.1.3 ListIterator listIterator() or ListIterator listIterator(int index)

这个就是List类的特有迭代器,除了Iterator中的方法,它有一些自己特色的方法。
向前遍历(必须在向后遍历之后)(所以基本不用):

  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. ListIterator it=d.listIterator();
  7. //当然,你用for循环也没问题= -
  8. while(it.hasNext()){//判断是否还有元素,没了就跳出
  9. //这里向下造型,当然你直接打印it.next()也没问题,
  10. //不过万一会用到String类型中的某些方法,这样就比较方便了.
  11. //从第一个元素开始拿,拿完之后自动指向下一个
  12. String s = (String)it.next();
  13. System.out.println(s);
  14. }
  15. //向前遍历,但必须在向后遍历之后才能执行
  16. //可以理解为它们共用一个指针,该指针初始在开头
  17. while(it.hasPrevious()){
  18. String s = (String)it.previous();
  19. System.out.println(s);
  20. }
  21. System.out.println(d);
  22. }
2.2 遍历

除了利用Collection里面的toArray以及iterator进行遍历外(因为继承了Collection,所以只要Collection能用的,它也能用),它由于自己身的数组特性,还有另外一个遍历方法

  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. for(int i=0;i<d.size();i++){//利用size方法
  7. System.our.println(d.get(i))//利用get方法。
  8. }
  9. }

3、迭代器问题

3.1 for循环还是while循环
  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. Iterator it=d.iterator();
  7. while(it.hasNext()){
  8. String s= (String)it.next();
  9. System.our.println(s)
  10. }
  11. }
  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. for(Iterator it=d.iterator();it.hasNext;){
  7. String s= (String)it.next();
  8. System.our.println(s)
  9. }
  10. }

两种方法差不多,都可以遍历,只不过for方法中的it在for执行完了就是垃圾,会被回收,所以效率会高一些。但是while方法会比较明确。

3.2 多次调用迭代器

为了不用向下转型,很多人会这么写

  1. class Student{
  2. private int age;
  3. private String name;
  4. public Student(int age, String name){
  5. this.age=age;
  6. this.name=name;
  7. }
  8. public int getage(){
  9. return age;
  10. }
  11. public String getname(){
  12. return name;
  13. }
  14. }
  15. public static void main(String[] args) {
  16. List d=new ArrayList();
  17. d.add(new Student(10,"Hello"));
  18. d.add(new Student(20,"World"));
  19. while(it.hasNext()){
  20. //这下打印的是10和world。
  21. //调用完next之后它会自动指向下一个
  22. //所以不要在一次循环中调用两次
  23. //用向下转型可以避免哦!!!!!
  24. System.our.println(it.next().getage+it.next().getage)
  25. }
  26. }
3.3 迭代中集合修改
  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("Hello");
  5. d.add("World");
  6. ListIterator it=d.listIterator();
  7. while(it.hasNext()){
  8. String s= (String)it.next();
  9. System.our.println(s)
  10. d.add("java");//这样会报错
  11. }
  12. }

由于it中存储的是修改前的集合信息,修改后的信息并没有实时更新到it中,这样就会导致it不认识现在的集合,所以报错。
解决办法1:调用ListIterator(注意,Iteratro里面没有!!)迭代器自己的add方法

  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("the");
  5. d.add("World");
  6. ListIterator it=d.listIterator();
  7. while(it.hasNext()){
  8. String s= (String)it.next();
  9. System.our.println(s);
  10. if(s.equals("the")){
  11. it.add("java");
  12. }
  13. }
  14. System.our.println(d)//输出 hello the java world加在当前
  15. }

解决方法2:用List特有的遍历方法,size+get+for循环

  1. public static void main(String[] args) {
  2. List d=new ArrayList();
  3. d.add("Hello");
  4. d.add("the");
  5. d.add("World");
  6. for(int i=0;i<d.size();i++){
  7. String s= d.get(i);
  8. System.our.println(s);
  9. if(s.equals("the")){
  10. it.add("java");
  11. }
  12. }
  13. System.our.println(d)//输出 hello the world java 加在最后
  14. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注