博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

06java进阶——集合框架(list和泛型)

Posted on 2019-01-29 21:19  心默默言  阅读(284)  评论(0编辑  收藏  举报

1.ArrayList

ArrayList集合是程序中最常见的一种集合,它属于引用数据类型(类)。在ArrayList内部封装了一个长度可变的数组,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素,因此可以将ArrayList集合看作一个长度可变的数组。

1.1ArrayList的创建

导包:import java.util.ArrayList;

创建对象:与其他普通的引用数据类型创建方式完全相同,但是要指定容器中存储的数据类型:

ArrayList<要存储元素的数据类型> 变量名 = new ArrayList<要存储元素的数据类型>();

  •  集合中存储的元素,只能为<>括号中指定的数据类型元素;
  • “<要存储元素的数据类型>”中的数据类型必须是引用数据类型,不能是基本数据类型;

下面给出8种基本数据类型所对应的引用数据类型表示形式:

我们通过举几个例子,来明确集合的创建方式:

  • 存储String类型的元素
ArrayList<String> list = new ArrayList<String>();
  • 存储int类型的数据
ArrayList<Integer> list = new ArrayList<Integer>();
  • 存储Phone类型的数据
  ArrayList<Phone> list = new ArrayList<Phone>();

1.2常用的方法

package cn.jxufe.java.chapter6;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestArrayList01 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // 创建ArrayList集合
        ArrayList<String> list1 = new ArrayList<String>();// 第二个可以省略
        // 向集合中添加元素

        list1.add("stu1");
        list1.add("stu2");
        list1.add("stu3");
        list1.add("stu4");
        // 获取集合中元素的个数
        // 取出并打印指定位置的元素
        System.out.println("第2个元素是:" + list1.get(1));
        System.out.println("list1中的元素为:");
        System.out.println(list1);
        System.out.println("去除索引值为2的元素后,list中的元素为");
        list1.remove(2);
        System.out.println(list1);
        System.out.println("在索引值为1的元素处添加一个student元素:");
        list1.add(1, "student");
        System.out.println(list1);
        System.out.println("把索引位置为3的元素置为good:");
        list1.set(3, "good");
        System.out.println(list1);
        // 集合的遍历
        System.out.println("集合的遍历方法1:");
        for (String e : list1) {
            System.out.print(e + "  ");
        }
        System.out.println("\n集合的遍历方法2: ");
        for (int i = 0; i < list1.size(); i++) {
            System.out.print(list1.get(i) + "  ");
        }
        System.out.println("\nlist1是否为空:");
        System.out.println(list1.isEmpty());
        System.out.println("list1是否包涵good:");
        System.out.println(list1.contains("good"));

        ArrayList<String> list2 = new ArrayList<>();
        list2.add("stu1");
        list2.add("stu2");
        System.out.println("list2中的元素为:");
        System.out.println(list2);
        System.out.println("从list1中移除list2中的全部元素");
        list1.removeAll(list2);// 获得两个集合元素的差集
        System.out.println(list1);
        System.out.println("list1添加list2中的全部元素");
        list1.addAll(list2);// 获得两个集合元素的并集
        System.out.println(list1);

        List<String> list3 = new ArrayList<String>();
        list3.add("第一个元素"); // 向列表中添加数据
        list3.add("第二个元素"); // 向列表中添加数据
        list3.add("第三个元素"); // 向列表中添加数据
        List<String> list4 = new ArrayList<String>();
        list4.add("第一个元素"); // 向列表中添加数据
        list4.add("第三个元素"); // 向列表中添加数据
        System.out.println("--------测试retainAll的使用------------:");
        boolean ret = list3.retainAll(list4); // 获得两集合相交的元素
        System.out.println(ret);
        System.out.println(list3);

        // 创建迭代器
        Iterator<String> it = list3.iterator();
        // 循环遍历迭代器
        System.out.println("循环遍历迭代器:");
        while (it.hasNext()) {
            System.out.println(it.next()); // 输出集合中元素
        }

        ArrayList<String> list5 = new ArrayList<>();
        ArrayList<String> list6 = new ArrayList<>();
        list5.add("a");
        list5.add("b");
        list5.add("c");
        list6.add("b");
        list6.add("c");
        list6.add("d");
        list5.retainAll(list6);// 获得两集合相交的元素
        System.out.println(list5);
    }
}

查看ArrayList类发现它继承了抽象类AbstractList同时实现接口List,而List接口又继承了Collection接口。Collection接口为最顶层集合接口了。

 源代码:
      interface List extends Collection {
      }
      public class ArrayList extends AbstractList implements List{
      }
    

集合继承体系
这说明我们在使用ArrayList类时,该类已经把所有抽象方法进行了重写。那么,实现Collection接口的所有子类都会进行方法重写。

  • Collecton接口常用的子接口有:List接口、Set接口
  • List接口常用的子类有:ArrayList类、LinkedList类
  • Set接口常用的子类有:HashSet类、LinkedHashSet类

2.迭代器

  java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

package cn.jxufe.java.chapter6;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class TestIterator03 {
    /*
     * 集合中的迭代器: 获取集合中元素方式 接口 Iterator : 两个抽象方法 boolean hasNext()
     * 判断集合中还有没有可以被取出的元素,如果有返回true next() 取出集合中的下一个元素
     * 
     * Iterator接口,找实现类. Collection接口定义方法 Iterator iterator() ArrayList
     * 重写方法iterator(),返回了Iterator接口的实现类的对象 使用ArrayList集合的对象 Iterator
     * it=array.iterator(),运行结果就是Iterator接口的实现类的对象 it是接口的实现类对象,调用方法 hasNext 和 next
     * 集合元素迭代
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Collection<String> coll = new ArrayList<String>();
        coll.add("abc1");
        coll.add("abc2");
        coll.add("abc3");
        coll.add("abc4");
        // 迭代器,对集合ArrayList中的元素进行取出

        // 调用集合的方法iterator()获取出,Iterator接口的实现类的对象
        Iterator<String> it = coll.iterator();
        // 接口实现类对象,调用方法hasNext()判断集合中是否有元素
        // boolean b = it.hasNext();
        // System.out.println(b);
        // 接口的实现类对象,调用方法next()取出集合中的元素
        // String s = it.next();
        // System.out.println(s);

        // 迭代是反复内容,使用循环实现,循环的条件,集合中没元素, hasNext()返回了false
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        System.out.println();
        // for循环的方式进行遍历,for没有while好理解,但是节约点内存,因为while的it是在main函数中建立的,for的it是在for中建立的
        for (Iterator<String> it2 = coll.iterator(); it2.hasNext();) {
            System.out.println(it2.next());
        }
    }

}

3.集合迭代中的转型

存储时提升了Object。取出时要使用元素的特有内容,必须向下转型。

package cn.jxufe.java.chapter6;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class TestCollection04 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Collection coll = new ArrayList();
        coll.add("abc");
        coll.add("aabbcc");
        coll.add("shitcast");
        Iterator it = coll.iterator();
        while (it.hasNext()) {
            // 由于元素被存放进集合后全部被提升为Object类型
            // 当需要使用子类对象特有方法时,需要向下转型
            String str = (String) it.next();
            System.out.println(str.length());
        }
        // 注意:如果集合中存放的是多个对象,这时进行向下转型会发生类型转换异常。
    }

}

4.泛型

4.1泛型的引入

在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。比如下面程序:

package cn.jxufe.java.chapter6;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestGeneric05 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List list = new ArrayList();
        list.add("abc");
        list.add("itcast");
        list.add(5);// 由于集合没有做任何限定,任何类型都可以给其中存放
                    // 相当于:Object obj=new Integer(5);

        Iterator it = list.iterator();
        while (it.hasNext()) {
            // 需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
            String str = (String) it.next();// String str=(String)obj;
                                            // 编译时期仅检查语法错误,String是Object的儿子可以向下转型
                                            // 运行时期String str=(String)(new Integer(5))
                                            // String与Integer没有父子关系所以转换失败
                                            // 程序在运行时发生了问题java.lang.ClassCastException
            System.out.println(str.length());
        }
    }
}

4.2泛型的定义和使用

 泛型: 指明了集合中存储数据的类型  <数据类型>

上面已经使用了

4.3Java中的伪泛型

泛型只在编译时存在,编译后就被擦除,在编译之前我们就可以限制集合的类型,起到作用
例如:ArrayList<String> al=new ArrayList<String>();
编译后:ArrayList al=new ArrayList();

4.4泛型类

a:定义格式:

修饰符 class 类名<代表泛型的变量> {  }
      
      例如,API中的ArrayList集合:
      class ArrayList<E>{ 
           public boolean add(E e){ }
        public E get(int index){  }
      }

 b:使用格式:

创建对象时,确定泛型的类型
     
      例如,ArrayList<String> list = new ArrayList<String>();
      此时,变量E的值就是String类型
      class ArrayList<String>{ 
        public boolean add(String e){ }
        public String get(int index){  }
      }
     
      例如,ArrayList<Integer> list = new ArrayList<Integer>();
      此时,变量E的值就是Integer类型
      class ArrayList<Integer>{ 
           public boolean add(Integer e){ }
           public Integer get(int index){  }
      }

package cn.jxufe.java.chapter6;

import java.util.ArrayList;

public class GenericStack07<E> {

    ArrayList<E> list = new ArrayList<>();

    public int getSize() {
        return list.size();
    }

    public E peek() {
        return list.get(getSize() - 1);
    }

    public E pop() {
        E o = list.get(getSize() - 1);
        list.remove(getSize() - 1);
        return o;
    }

    public boolean isEmpty() {
        return list.isEmpty();
    }

    public void push(E e) {
        list.add(e);
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "stack: " + list.toString();
    }

    public static void main(String[] args) {
        GenericStack07<String> stack1 = new GenericStack07<>();
        stack1.push("good");
        stack1.push("good");
        stack1.push("study");
        System.out.println(stack1);
        
        GenericStack07<Integer> stack2 = new GenericStack07<>();
        stack2.push(123);
        stack2.push(456);
        stack2.push(789);
        System.out.println(stack2);
    }
}

4.5泛型的方法

  • a:定义格式:修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }
  • b:泛型方法的使用:
  例如,API中的ArrayList集合中的方法:
      public <T> T[] toArray(T[] a){  } 
      //该方法,用来把集合元素存储到指定数据类型的数组中,返回已存储集合元素的数组

      使用格式:调用方法时,确定泛型的类型
  例如:
          ArrayList<String> list = new ArrayList<String>();
          String[] arr = new String[100];
          String[] result = list.toArray(arr);
       此时,变量T的值就是String类型。变量T,可以与定义集合的泛型不同
       public <String> String[] toArray(String[] a){  } 

  例如:
          ArrayList<String> list = new ArrayList<String>();
          Integer[] arr = new Integer[100];
          Integer [] result = list.toArray(arr);
      
      此时,变量T的值就是Integer类型。变量T,可以与定义集合的泛型不同
      public <Integer> Integer[] toArray(Integer[] a){  } 

 使用泛型类型来定义泛型方法

package cn.jxufe.java.chapter6;

public class TestGenericMethod {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Integer[] integers = { 1, 2, 3, 4, 5 };
        String[] strings = { "london", "xuzhou", "nanchang" };
        print(integers);
        print(strings);
    }

    public static <E> void print(E[] list) {
        for (E e : list) {
            System.out.print(e + " ");
        }
        System.out.println();
    }

}

4.6泛型的接口 

/*
      *  带有泛型的接口
      *  
      *  public interface List <E>{
      *    abstract boolean add(E e);
      *  }
      * 
      *  实现类,先实现接口,不理会泛型
      *  public class ArrayList<E> implements List<E>{
      *  }
      *  调用者 : new ArrayList<String>() 后期创建集合对象的时候,指定数据类型
      *  
      *  
      *  实现类,实现接口的同时,也指定了数据类型
      *  public class XXX implements List<String>{
      *  }
      *  new XXX()
      */

4.7受限的泛型类型

可以将泛型指定为另一种类型的子类型,这样的泛型类型称为受限的。

package cn.jxufe.java.chapter6;

abstract class GeometricObject {
    public abstract double getArea();
}

class Rectangle extends GeometricObject {
    double length;
    double hight;

    public Rectangle(double length, double hight) {
        // TODO Auto-generated constructor stub
        this.length = length;
        this.hight = hight;
    }

    public double getArea() {
        return length * hight;
    }
}

class Circle extends GeometricObject {
    double radius;

    public Circle(double radius) {
        // TODO Auto-generated constructor stub
        this.radius = radius;
    }

    public double getArea() {
        return 3.14 * radius * radius;
    }
}

public class TestBoundedType {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Rectangle rectangle = new Rectangle(2, 2);
        Rectangle rectangle2 = new Rectangle(2, 2);
        Circle circle = new Circle(2);
        System.out.println(equalArea(rectangle, circle));
        System.out.println(equalArea(rectangle, rectangle2));
    }

    public static <E extends GeometricObject> boolean equalArea(E object1, E object2) {
        return object1.getArea() == object2.getArea();
    }

}

4.8示例学习:对一个对象数组进行排序

 

package cn.jxufe.java.chapter6;

public class TestGenericSort {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Integer[] intArray = { new Integer(2), new Integer(4), new Integer(3) };
        Double[] doubleArray = { new Double(3.4), new Double(1.3), new Double(-22.1) };
        Character[] charArray = { new Character('a'), new Character('c'), new Character('b') };
        String[] stringArray = { "tom", "jack", "blue" };
        sort(intArray);
        sort(doubleArray);
        sort(charArray);
        sort(stringArray);

        System.out.println("sorted integer objects:");
        printList(intArray);
        System.out.println("sorted double objects:");
        printList(doubleArray);
        System.out.println("sorted charArray objects:");
        printList(charArray);
        System.out.println("sorted stringArray objects:");
        printList(stringArray);
    }

    public static <E extends Comparable<E>> void sort(E[] list) {
        E currentMin;
        int currentMinIndex;
        for (int i = 0; i < list.length - 1; i++) {
            currentMin = list[i];
            currentMinIndex = i;
            for (int j = i + 1; j < list.length; j++) {
                if (currentMin.compareTo(list[j]) > 0) {
                    currentMin = list[j];
                    currentMinIndex = j;
                }
            }
            if (currentMinIndex != i) {
                list[currentMinIndex] = list[i];
                list[i] = currentMin;
            }
        }
    }

    public static void printList(Object[] list) {
        for (int i = 0; i < list.length; i++) {
            System.out.print(list[i] + " ");
        }
        System.out.println();
    }

}

4.9泛型的好处

  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
  • 避免了类型强转的麻烦。

4.10泛型的通配符

 

package cn.jxufe.java.chapter6;

public class TestWildCard {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        GenericStack07<Integer> intStack = new GenericStack07<>();
        intStack.push(1);
        intStack.push(2);
        intStack.push(-1);
        
        System.out.println("the max number is " + max(intStack));//编译会报错,因为intStack不是GenericStack<Number>的实例
    }

    public static double max(GenericStack07<Number> stack) {
        double max = stack.pop().doubleValue();
        while (!stack.isEmpty()) {
            double value = stack.pop().doubleValue();
            if (value > max)
                max = value;
        }
        return max;
    }

}

package cn.jxufe.java.chapter6;

public class TestWildCard {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        GenericStack07<Integer> intStack = new GenericStack07<>();
        intStack.push(1);
        intStack.push(2);
        intStack.push(-1);
        
        System.out.println("the max number is " + max(intStack));//编译会报错,因为intStack不是GenericStack<Number>的实例
    }

    public static double max(GenericStack07<? extends Number> stack) {
        double max = stack.pop().doubleValue();
        while (!stack.isEmpty()) {
            double value = stack.pop().doubleValue();
            if (value > max)
                max = value;
        }
        return max;
    }

}

package cn.jxufe.java.chapter6;

public class TestAnyWild {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        GenericStack07<Integer> intStack = new GenericStack07<>();
        intStack.push(1);
        intStack.push(2);
        intStack.push(-2);
        print(intStack);
        
        GenericStack07<String> stringStack = new GenericStack07<>();
        stringStack.push("zhangSan");
        stringStack.push("zhaoSi");
        stringStack.push("wangWu");
        print(stringStack);
        

    }

    public static void print(GenericStack07<?> stack) {
        while (!stack.isEmpty()) {
            System.out.print(stack.pop() + " ");
        }
        System.out.println();
    }

}

package cn.jxufe.java.chapter6;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class TestWildcard06 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArrayList<String> array = new ArrayList<String>();

        HashSet<Integer> set = new HashSet<Integer>();

        array.add("123");
        array.add("456");

        set.add(789);
        set.add(890);

        iterator(array);
        iterator(set);
    }

    /*
     * 定义方法,可以同时迭代2个集合 参数: 怎么实现 , 不能写ArrayList,也不能写HashSet 参数: 或者共同实现的接口
     * 泛型的通配,匹配所有的数据类型 ?
     */
    public static void iterator(Collection<?> coll) {
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            // it.next()获取的对象,什么类型
            System.out.println(it.next());
        }
    }
}

 

package cn.jxufe.java.chapter6;

public class TestSuperWildChard {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        GenericStack07<String> stack1 = new GenericStack07<>();
        GenericStack07<Object> stack2 = new GenericStack07<>();
        stack2.push("java");
        stack2.push(2);
        stack1.push("sun");
        add(stack1, stack2);
        TestAnyWild12.print(stack2);
    }

    public static <T> void add(GenericStack07<T> stack1, GenericStack07<? super T> stack2) {
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
    }

}