集合框架(List和Set)

来源:互联网 发布:app php服务器搭建 编辑:程序博客网 时间:2024/05/20 12:51

一、概述

集合是一种可变数据项的容器,具有统一的父类接口Collection<E>Map并没有继承之),与其子集合的关系如下 图,集合的特点是长度可变,可以存储多种类型的对象(不加泛型时)。这也是与数组的两点最大的不同。

这里写图片描述 java集合类关系图

Collection最为根接口,ListSetQueue接口均直接继承自它,Map接口虽然不是直接继承自Collection,但是接口中使用到了Collection,即Map的数据也是使用Collection存储的。
研究集合必不可少的是先研究Collection接口的内容,先从接口名字定义开始说起public interface Collection<E> extends Iterable<E>,这是集合的根接口,<E>是JDK1.5开始的泛型,其功能是提高安全性,增强针对性,具体介绍后面再说。Iterable<E>接口是迭代器,下面有具体介绍。Collection接口提供了一些集合的共性方法,如:

  • boolean add(E e)添加一条数据。
  • boolean addAll(Collection<? extends E> c)添加另一个集合内的元素。
  • void clear()清除所有数据。
  • boolean contains(Object o)是否包含指定数据。
  • Iterator<E> iterator()返回迭代器。
  • boolean remove(Object o)删除指定数据。
  • <T>T[] toArray(T[] a)返回数据的数组形式等等。
  • boolean retainAll(Collection<?> c)取与集合c的交集。
  • boolean removeAll(Collection<?> c)取与集合c的差集。

二、迭代器

一种集合的取出方式,用于集合的数据的遍历等。不同容器的数据结构不同,所以取出的动作实现不同,具体的取出动作定义在每个容器的内部,这样取出方式就可以直接访问集合内部元素,取出方式就被定义成了内部类。但是都有共性内容判断和取出,共性抽取便形成了Iterator接口(迭代器)。
迭代器的使用示例(暂时不考虑泛型):

ArrayList alist = new ArrayList();alist.add("java01");alist.add("java02");alist.add("java03");alist.add("java04");for(Iterator it = alist.iterator(); it.hasNext(); ) {    System.out.println(it.next());}

三、List

List为Collection的常见子接口之一,元素有序,可以重复,有索引。
List有些特有的方法,和下标相关的一些方法,如:
- 增:add(index, element)指定位置添加元素,addAll(index, Collection)指定位置添加一个集合的元素。
- 删:remove(index)删除指定为的元素。
- 改:set(index, element)修改指定位置的元素。
- 查:get(index)获取指定位置的元素,subList(from, to)获取[from, to)段的子ListlistIterator()获取List特有的迭代器,indexOf(E)获取对象的位置。

ListIterator

ListIteratorIterator的子接口,在迭代时不可以通过集合的操作对集合进行操作,会发生并发修改异常,但是Iterator的操作是有限的,如果想对元素进行添加和修改等,就需要用到其子接口ListIterator,该接口只能通过ListlistIterator()方法获取,示例代码如下:

ArrayList alist = new ArrayList();alist.add("java01");alist.add("java02");alist.add("java03");alist.add("java04");for(ListIterator it = alist.listIterator(); it.hasNext(); ) {    System.out.println(it.next());    it.add("java");}System.out.println(alist);//输出结果是java01java02java03java04[java01, java, java02, java, java03, java, java04, java]

通过上面可以看到,ListIterator在获取之后其遍历的内容就已经确定下来了。 ListIterator还有逆向遍历功能。

ArrayList alist = new ArrayList();alist.add("java01");alist.add("java02");alist.add("java03");alist.add("java04");ListIterator it = alist.listIterator();while(it.hasNext()) {    System.out.println(it.next());}while(it.hasPrevious()) {    System.out.println(it.previous());}// 输出的结果是java01java02java03java04java04java03java02java01

List中具体对象特点

List有三个具体的子类,分别是ArrayListLinkedListVector。三者的特点与不同如下:

  • ArrayList:底层数据结构为数组结构,线程非同步,查询速度快,增删速度慢,效率比Vector高,最早出现在JDK1.2版本。初始长度默认为10,空间不足时50%延长。
  • LinkedList:底层数据结构为链表结构,线程非同步,查询速度慢,增删速度快,最早出现在JDK1.2版本。
  • Vector:底层数据结构是数组结构,线程同步,与ArrayList有相同的功能,已被ArrayList代替,最早出现在JDK1.0版本。默认长度为10,空间不足时100%延长。

Vector的特点

Vector的特有的一点就是,Vector的枚举(Vector的特有取出方式),和迭代器类似,名为EnumerationArrayList中没有此功能,但是由于方法名过长,且没有移除功能,已被Iterator代替,所以一般优先考虑使用Iterator,示例代码如下:

Vector v = new Vector();v.add("java01");v.add("java02");v.add("java03");Enumeration en = v.elements();while(en.hasMoreElements()) {    System.out.println(en.nextElement());}// 输出结果为java01java02java03

LinkedList

LinkedList的特有方法:addFirst(E e)addLast(E e)添加到头部或者尾部、getFirst()getLast()获取头部元素或者尾部元素、removeFirst()removeLast()删除头部元素或者尾部元素,并返回删除的元素,没有会报NoSuchElementException,这些是LinkedList所特有的方法。JDK1.6版本名称发生了变化,分别变成了offerFirst(E e)offerLast(E e)peekFirst()peekLast()pollFirst()pollLast(),没有会返回null
LinkedList练习,模拟队列进行添加和取出动作。示例代码如下:

import java.util.*;class MyQueue {    private LinkedList link;    MyQueue() {        link = new LinkedList();    }    public void myAdd(Object obj) {        link.addFirst(obj);    }    public Object myGet() {        return link.removeFirst();    }    public boolean isNull() {        return link.isEmpty();    }}class LinkedListTest {    public static void main(String[] args) {        MyQueue q = new MyQueue();        q.myAdd("java01");        q.myAdd("java02");        q.myAdd("java03");        q.myAdd("java04");        while(!q.isNull()) {            System.out.println(q.myGet());        }    }}

ArrayList

ArrayList练习,去除ArrayList中的重复项。代码示例如下:

import java.util.*;class ArrayListTest {    public static void main(String[] args) {        ArrayList list = new ArrayList();        list.add("java01");        list.add("java02");        list.add("java01");        list.add("java02");        list.add("java01");        list.add("java03");        // 去除重复前        System.out.println(list);        list = singleElement(list);        // 去除重复后        System.out.println(list);    }    public static ArrayList singleElement(ArrayList list) {        //定义一个临时容器。        ArrayList newList = new ArrayList();        // 获取迭代器        Iterator it = list.iterator();        while(it.hasNext()) {            Object obj = it.next();            if(!newList.contains(obj)) {                newList.add(obj);            }        }        return newList;    }}

ArrayList练习,自定义类添加至ArrayList,需要覆盖自定义类的equals方法。

import java.util.*;class Person {    private String name;    private int age;    Person(String name, int age) {        this.name = name;        this.age = age;    }    // 覆盖equals方法    public boolean equals(Object obj) {        if(!(obj instanceof Person))            return false;        Person p = (Person)obj;        return this.name.equals(p.name) && this.age == p.age;    }    public String getName() {        return name;    }    public int getAge() {        return age;    }    public String toString() {        return "Name:" + this.name + " Age:" + this.age;    }}class ArrayListTest {    public static void main(String[] args) {        ArrayList list= new ArrayList();        list.add(new Person("lisi01",30));        list.add(new Person("lisi02",32));        list.add(new Person("lisi02",32));        list.add(new Person("lisi04",35));        list.add(new Person("lisi03",33));        list.add(new Person("lisi04",35));        System.out.println(list);        list = singleElement(list);        System.out.println(list);    }    public static ArrayList singleElement(ArrayList al) {        // 定义一个临时容器。        ArrayList newList = new ArrayList();        // 取出迭代器        Iterator it = al.iterator();        while(it.hasNext()) {            Object obj = it.next();            if(!newList.contains(obj))                newList.add(obj);        }        return newList;    }}

四、Set

元素无序(存入与取出的顺序不一致),元素不可重复。有两个主要子类:HashSetTreeSet

  • HashSet:底层数据结构为哈希表。
  • TreeSet:底层数据结构为二叉树。

HashSet

根据hashCode()equals()方法进行存储。add时,如果是第一次添加返回true(添加成功),否则返回false(添加失败)。示例代码如下:

import java.util.*;class HashSetDemo {     public static void main(String[] args) {        HashSet hs = new HashSet();        // 第一次添加java01返回true        System.out.println(hs.add("java01"));        // 第二次添加java01返回false        System.out.println(hs.add("java01"));        hs.add("java02");        hs.add("java03");        hs.add("java03");        hs.add("java04");        // 遍历集合        Iterator it = hs.iterator();        while(it.hasNext()) {            System.out.println(it.next());        }    }}

HashSet存储自定义对象,自定义对象需要覆盖hashCodeequals方法,当hashCode值相同、equals返回真时,认为是同一个对象。添加时,当hashCode值不同时,不会调用equals比较,当hashCode的值相同时,才会调用equals方法比较两个对象。示例代码如下:

import java.util.*;/** *姓名和年龄相同认为是同一个人 */class Person {    private String name;    private int age;    Person(String name,int age) {        this.name = name;        this.age = age;    }    public int hashCode() {        System.out.println(this.name+"....hashCode");        return name.hashCode() + age*37;    }    public boolean equals(Object obj) {        if(!(obj instanceof Person))            return false;        Person p = (Person)obj;        System.out.println(this.name+"...equals.."+p.name);        return this.name.equals(p.name) && this.age == p.age;    }    public String getName() {        return name;    }    public int getAge() {        return age;    }}class HashSetTest {    public static void main(String[] args) {        HashSet hs = new HashSet();        hs.add(new Person("a1",11));        hs.add(new Person("a2",12));        hs.add(new Person("a3",13));        // 添加一个相同的人        hs.add(new Person("a2",12));        hs.add(new Person("a4",14));        // 遍历集合中的人        Iterator it = hs.iterator();        while(it.hasNext()) {            Person p = (Person)it.next();            System.out.println(p.getName()+"::"+p.getAge());        }    }}// 运行结果为a1....hashCodea2....hashCodea3....hashCodea2....hashCodea2...equals..a2a4....hashCodea3::13a1::11a4::14a2::12

从上面代码运行的结果可以看出,当添加new Person("a2",12)时,因为集合中已经有这个人了,所以hashCode的返回值已经存在,因此便会调用equals方法判断,最终结果还是此人已存在,所以最总集合中只有4个人。
HashSet的判断和删除依据,可以看到判读元素是否存在和删除元素都是首先根据hashCode值,然后再通过equals判断是否相同。

import java.util.*;/** *姓名和年龄相同认为是同一个人 */class Person {    private String name;    private int age;    Person(String name,int age) {        this.name = name;        this.age = age;    }    /**     *统一返回1作为哈希值     */    public int hashCode() {        System.out.println("hashCode");        return 1;    }    /**     * 姓名和年龄相同认为是同一个人     */    public boolean equals(Object obj) {        if(!(obj instanceof Person))            return false;        Person p = (Person)obj;        System.out.println(this.name+"...equals.."+p.name);        return this.name.equals(p.name) && this.age == p.age;    }    public String getName() {        return name;    }    public int getAge() {        return age;    }}class HashSetTest {    public static void main(String[] args) {        HashSet hs = new HashSet();        hs.add(new Person("a1",11));        hs.add(new Person("a2",12));        hs.add(new Person("a3",13));        // 添加一个相同的人        hs.add(new Person("a2",12));        hs.add(new Person("a4",14));        System.out.println("--------");        // 判断是否存在元素        hs.contains(new Person("a4", 14));        System.out.println("--------");        // 删除指定元素        hs.remove(new Person("a1", 11));    }}// 运行结果为hashCodehashCodea2...equals..a1hashCodea3...equals..a1a3...equals..a2hashCodea2...equals..a1a2...equals..a2hashCodea4...equals..a1a4...equals..a2a4...equals..a3--------hashCodea4...equals..a1a4...equals..a2a4...equals..a3a4...equals..a4--------hashCodea1...equals..a1

TreeSet

可以对集合中的元素进行排序。底层数据结构为二叉树,中序遍历便是TreeSet的顺序。示例代码如下:

import java.util.*;class TreeSetDemo {    public static void main(String[] args) {        TreeSet ts = new TreeSet();        ts.add("asdas");        ts.add("abc");        ts.add("beds");        ts.add("abd");        Iterator it = ts.iterator();        while(it.hasNext()) {            System.out.println(it.next());        }    }}// 运行结果为abcabdasdasbeds

对于自定义对象的排序,一般有两种做法,一是自定义对象自己实现Comparable接口,二是集合提供比较器(Comparator接口)。
自定义对象实现Comparable接口,示例代码如下:

import java.util.*;class Student implements Comparable {    private String name;    private int age;    Student(String name,int age) {        this.name = name;        this.age = age;    }    /**     *比较结果又三种,负数表示小于,0表示等于,正数表示大于     */    public int compareTo(Object obj) {        if(!(obj instanceof Student))            throw new RuntimeException("不是学生对象");        Student s = (Student)obj;        System.out.println(this.name+"....compareto....."+s.name);        if(this.age>s.age)            return 1;        if(this.age==s.age) {            // String类已经实现了Comparable接口            return this.name.compareTo(s.name);        }        return -1;    }    public String getName() {        return name;    }    public int getAge() {        return age;    }}class TreeSetDemo {    public static void main(String[] args) {        TreeSet ts = new TreeSet();        ts.add(new Student("lisi02",22));        ts.add(new Student("lisi007",20));        ts.add(new Student("lisi09",19));        ts.add(new Student("lisi08",19));        ts.add(new Student("lisi007",20));        ts.add(new Student("lisi01",40));        // 遍历集合        Iterator it = ts.iterator();        while(it.hasNext()) {            Student stu = (Student)it.next();            System.out.println(stu.getName()+"..."+stu.getAge());        }    }}

集合提供比较器(Comparator接口)的比较方式,用于元素不具备比较性或者具备的比较性不是用户所需要的时。示例代码如下:

import java.util.*;class Student implements Comparable {    private String name;    private int age;    Student(String name,int age) {        this.name = name;        this.age = age;    }    /**     *比较结果又三种,负数表示小于,0表示等于,正数表示大于     */    public int compareTo(Object obj) {        if(!(obj instanceof Student))            throw new RuntimeException("不是学生对象");        Student s = (Student)obj;        System.out.println(this.name+"....compareto....."+s.name);        if(this.age>s.age)            return 1;        if(this.age==s.age) {            return this.name.compareTo(s.name);        }        return -1;    }    public String getName() {        return name;    }    public int getAge() {        return age;    }}class TreeSetDemo2 {    public static void main(String[] args) {        TreeSet ts = new TreeSet(new MyCompare());        ts.add(new Student("lisi02",22));        ts.add(new Student("lisi02",21));        ts.add(new Student("lisi007",20));        ts.add(new Student("lisi09",19));        ts.add(new Student("lisi06",18));        ts.add(new Student("lisi06",18));        ts.add(new Student("lisi007",29));        ts.add(new Student("lisi007",20));        ts.add(new Student("lisi01",40));        Iterator it = ts.iterator();        while(it.hasNext()) {            Student stu = (Student)it.next();            System.out.println(stu.getName()+"..."+stu.getAge());        }    }}class MyCompare implements Comparator {    public int compare(Object o1,Object o2) {        Student s1 = (Student)o1;        Student s2 = (Student)o2;        // 姓名比较优先        int num = s1.getName().compareTo(s2.getName());        if(num==0) {            // 姓名相同时,年龄小的在前            return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));        }        return num;    }}

五、泛型

泛型的出现解决了类型不同的安全问题,JDK1.5版本开始出现的。具体做法就是在集合定义时指明数据类型,如ArrayList<String> list= new ArrayList<String>(),这样一来如果向list中添加非String类型对象时,就会在编译时期提示ClassCastException异常。示例代码如下:

import java.util.*;class GenericDemo {    public static void main(String[] args) {        ArrayList<String> list = new ArrayList<String>();        list.add("abc01");        list.add("abc0991");        list.add("abc014");        // Iterator也有对应的泛型        Iterator<String> it = al.iterator();        while(it.hasNext()) {            System.out.println(s+":"+s.length());        }    }}

自定义类中使用泛型的概念。试用于用数据类型不确定时,示例代码如下:

class Worker {}class Student {}/** *泛型类,使用与引用数据类型不确定时。 */class Utils<Q> {    private Q q;    public void setObject(Q q) {        this.q = q;    }    public Q getObject() {        return q;    }}class  GenericDemo3 {    public static void main(String[] args) {        Utils<Worker> u = new Utils<Worker>();        u.setObject(new Worker());        Worker w = u.getObject();;    }}

泛型定义在方法上,示例代码如下:

class Demo {    public <T> void show(T t) {        System.out.println("show:"+t);    }    public <Q> void print(Q q) {        System.out.println("print:"+q);    }    /**     * 静态方法不可以访问类上的泛型,可使用静态泛型方法,注意:泛型标识<E>需放在返回值前。     */    public static <E> void printf(E e) {        System.out.println("printf:"+e);    }}class GenericDemo4 {    public static void main(String[] args) {        Demo demo = new Demo();        demo.show("Hi");        demo.print("Hello");        Demo.printf(5);    }}//执行结果为show:Hiprint:Helloshow:5

泛型限定

一般有三种情况,

  • <?>:?为通配符。
  • <? extends E>:E和E的子类,称为上限。
  • <? super E>:E和E的父类,称为下限。

如对所有类型的ArrayList进行遍历输出,示例代码如下:

public static void print(ArrayList<?> list) {    Iterator<?> it = list.iterator();    while(it.hasNext()) {        System.out.println(it.next().toString());    }}

Person或者Person的子类Student进行遍历并输出,示例代码如下:

public static void print(ArrayList<? extends Person> list) {    Iterator<? extends Person> it = list.iterator();    while(it.hasNext()) {        System.out.println(it.next().getName());    }}

对于<? super E>并不常用,举一个TreeSet里的例子,TreeSet(Comparator<? super E> comparator),使用父类的比较器进行子类的比较。示例代码如下:

import java.util.*;class Person {    private String name;    Person(String name) {        this.name = name;    }    public String getName() {        return name;    }    public String toString() {        return "Person :"+name;    }}class Student extends Person {    Student(String name) {        super(name);    }}class Worker extends Person {    Worker(String name) {        super(name);    }}/** * 定义使用与Person的比较器 */class Comp implements Comparator<Person> {    public int compare(Person p1,Person p2) {        // 倒序        return p2.getName().compareTo(p1.getName());    }}class GenericDemo {    public static void main(String[] args) {        // 使用使用父类的比较器进行比较        TreeSet<Student> ts = new TreeSet<Student>(new Comp());        ts.add(new Student("abc03"));        ts.add(new Student("abc02"));        ts.add(new Student("abc06"));        ts.add(new Student("abc01"));        Iterator<Student> it = ts.iterator();        while(it.hasNext()) {            System.out.println(it.next().getName());        }        // 使用使用父类的比较器进行比较        TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());        ts1.add(new Worker("wabc--03"));        ts1.add(new Worker("wabc--02"));        ts1.add(new Worker("wabc--06"));        ts1.add(new Worker("wabc--01"));        Iterator<Worker> it1 = ts1.iterator();        while(it1.hasNext()) {            System.out.println(it1.next().getName());        }    }}// 执行结果为abc06abc03abc02abc01wabc--06wabc--03wabc--02wabc--01
1 0
原创粉丝点击