Java 集合类 1-Collection接口以及List子接口

时间:2021-01-23 17:02:49

  在实际开发中,数组的出现频率并不高,因为数组有一个很大的缺陷:数组长度固定。所以从JDK1.2开始,为了解决Java数组长度的问题,提供了动态的数组实现框架——Java集合类框架。

  Java集合类框架实际上就是针对于数据结构的一种实现。

  在Java的集合类库里面(java.util)包含了两个核心接口,Collection与Map。本次我们介绍的是Collection接口。

1. Collecion接口

  首先我们看一下JDK给出的Collection接口定义:

public interface Collection<E> extends Iterable<E>

  Collection接口是一个泛型接口,可以很好的避免向下转型时的ClassCastException异常。并且继承了Iterable接口,继承该接口的用途下面会讲到。

  另外,Collection接口是保存单个数据的集合最大父接口。即Collection集合每次只能保存一个数据,这个数据可以是基本数据类型,自定义数据类型(自定义的类也可以),但每次只能保存一个,不能一次保存多个,但可以保存多次啊。而且,实现Collection接口的子类也遵循该性质。

  Collection接口定义了如下常用方法:

// 返回集合长度
public int size();

// 集合是否为空,空返回true,反之false
public boolean isEmpty();

// 集合中是否包含指定元素
boolean contains(Object o);

// 返回一个Iterator接口对象,用于集合的输出,后面会讲
Iterator<E> iterator();

// 将集合编程对象数组返回
Object[] toArray();

// 将指定元素添加进集合中
boolean add(E e);

// 在集合中删除指定元素
boolean remove(Object o);

  虽然Collection接口定义了如此众多的方法,但是在实际工程中我们并不会直接使用它,而是使用它的子接口List,Set。

  Colletion接口继承关系如下:

Java 集合类 1-Collection接口以及List子接口

  Collection接口中定义的方法,子接口都有。

2. List接口

  我们首先看看List接口的定义:

public interface List<E> extends Collection<E>

  在实际开发中,List接口的使用占到了Collection集合系列的80%以上,在进行集合处理的时候,优先使用List接口。

  因为在List接口中定义了两个很有用的扩充方法:

// 取得集合中指定下标的元素
E get(int index);

// 设置集合中指定下标元素
E set(int index, E element);

  List接口有三个常用子类,分别为ArrayList,Vector,LinkedList。

  继承关系如下:

Java 集合类 1-Collection接口以及List子接口

  接下来我们详细介绍一下这三个子类。

2.1 ArrayList类(重要)

  ArrayList类可以添加重复的元素,也可以装入空(null)。

  ArrayList是针对List接口的一个实现类,底层由数组实现,对应数据结构中的顺序表。因为它是Collection接口的实现类,所以自然是可以自动扩容,长度可变的。

  JDK给出的ArrayList类定义以及常用方法:

// 类定义
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable // List接口的扩充get方法 public E get(int index) // List接口的扩充set方法 public E set(int index, E element)

  关于ArrayList类中方法的具体实现大家可以参考一下这篇文章:

ArrayList顺序表实现

  我们看下面的例子:

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

public class Test {
    public static void main(String[] args) {
        // 定义元素类型为String的ArrayList集合类
        List<String> list = new ArrayList<>();
        // 给集合类中添加元素,使用Collectio接口中定义的方法add
        list.add("hello");
        list.add("world");
        list.add("hello");
        list.add("java");
        list.add(null);
        // 这里为了简单起见,我们直接打印集合类,实际上,我们应该使用Iterator接口的对象进行集合类的输出
        System.out.println(list);   
    }
}

运行结果:

Java 集合类 1-Collection接口以及List子接口

  这也证明了ArrayList可以添加重复元素和null。

  我们再测试一下ArrayList的其他方法

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

public class Test {
    public static void main(String[] args) {
        // 定义元素类型为String的ArrayList集合类
        List<String> list = new ArrayList<>();
        // 给集合类中添加元素,使用Collectio接口中定义的方法add
        list.add("hello");
        list.add("world");
        list.add("hello");
        list.add("java");
        list.add(null);

        // size()
        System.out.println("list size is : "+list.size());
        // isEmpty()
        System.out.println("list.isEmpty(): "+list.isEmpty());
        // remove()
        System.out.println("list.remove(): "+list.remove("world"));
        System.out.println("after remove [world] :"+list);
        // contains()
        System.out.println("list.contains(): "+list.contains("world"));

        // get()方法进行集合元素输出
        System.out.println("print by list.get() :");
        for(int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
    }
}

运行结果:

Java 集合类 1-Collection接口以及List子接口

  另外注意一点的就是,我们声明ArrayList的时候的格式为:

List<T> list = new ArrayList<>();

  最好不要采用:

Collection<T> list = new ArrayList<>();

  虽然ArrayList是Collection接口的实现子类,但是List接口里对Collection接口进行了方法的扩充,而且这样的转换还要设计向下转型,所以最好使用List接口进行ArrayList对象的接收。

  ArrayList集合类除了接收基本数据类型,也可以接收自定义数据类型,如下面的例子:

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

class Person {
    public int age;
    public String name;
    public Person(String name, int age) {
        super();
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "[name: "+this.name+", age: "+this.age+"]";
    }
}

public class Test {
    public static void main(String[] args) {
        // 定义元素类型为String的ArrayList集合类
        List<Person> list = new ArrayList<>();
        // 给集合类中添加元素,使用Collectio接口中定义的方法add
        list.add(new Person("xucc", 10));
        list.add(new Person("lixx", 10));
        list.add(new Person("zhss", 10));

        // get()方法进行集合元素输出
        System.out.println("print by list.get() :");
        for(int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

运行结果:

Java 集合类 1-Collection接口以及List子接口

  最后要注意一点:ArrayList类的remove,contains都调用了对象的equals方法,如果没有覆写equals至想要的效果,会导致这两个方法无法判断出指定的对象,导致结果出现偏差。

2.2 Vector类(使用较少)

  Vector类是JDK1.0提出的,而上面介绍了的ArrayList是JDK1.2提出的。它的定义如下:

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

  Vector类的的使用方法,底层实现形式都与ArrayList类一致,这里就不做赘述了。但是在细节上,两者还是有很大差距的:

  • ArrayList是异步处理,性能更高。而Vector是同步处理,性能较低。
  • ArrayList是非线程安全,Vector是线程安全(Vector线程安全的手段非常暴力,即所有的方法全加锁,为synchronized方法,虽然很安全,但这也导致了Vector效率极其低下)。
  • 在输出形式上,ArrayLit支持Iterator,ListIterator,foreach三中输出方式。Vector拥有这三种之外,还多了一种,Enumeration(枚举输出)

  在以后使用的时候优先考虑ArrayList,因为其性能更高,实际开发时很多时候也是每个线程拥有自己独立的集合资
源。如果需要考虑同步也可以使用concurrent包提供的工具将ArrayList变为线程安全的集合。

  虽然日常中Vector的出场率不高,但是它却有一个很重要的子类——Stack类。

2.3 LinkedList类

  LinkedList的底层实现为双链表,使用发发与ArrayList一模一样。

  定义如下:

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable

  ArrayList封装的是数组,LinkedList封装的是链表。

  ArrayList的remove时间复杂度为O(n),效率较低,但是查找一个元素get时间复杂度为O(1)。

  LinkedList的remove时间复杂度O(1),效率高,但是查找一个元素get时间复杂度为O(n)。