Java-Python-Django社区程序员童心未泯

【JavaSE(九)】Java集合框架(上)

2018-05-24  本文已影响148人  苍云横渡

原文地址:https://www.cloudcrossing.xyz/post/41/

1 对象数组

数组既可以存储基本数据类型,也可以存储引用类型。它存储引用类型时的数组就叫对象数组。

举个例子:我有5个学生,请把这个5个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息。

学生:Student
成员变量:name,age
构造方法:无参,带参
成员方法:getXxx()/setXxx()

分析:

public class Student {
    // 成员变量
    private String name;
    private int age;

    // 构造方法
    public Student() { super();}
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    // 成员方法
    // getXxx()/setXxx()
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}
public class ObjectArrayDemo {
    public static void main(String[] args) {
        // 创建学生数组(对象数组)。
        Student[] students = new Student[5];

        // 创建5个学生对象,并赋值。
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("风清扬", 30);
        Student s3 = new Student("刘意", 30);
        Student s4 = new Student("赵雅芝", 60);
        Student s5 = new Student("王力宏", 35);

        // 把C步骤的元素,放到数组中。
        students[0] = s1;
        students[1] = s2;
        students[2] = s3;
        students[3] = s4;
        students[4] = s5;

        // 遍历
        for (int x = 0; x < students.length; x++) {
            //System.out.println(students[x]); //默认调用toString()
            Student s = students[x];
            System.out.println(s.getName()+"---"+s.getAge());
        }
    }
}

上述代码内存图解如下:


2 集合Collection

2.1 集合Collection概述

面向对象语言对事物的描述是通过对象体现的,为了方便对多个对象进行操作,我们就必须把这多个对象存储到一个容器类型的变量,目前所学过的知识里面容器类型的有数组和StringBuffer。但是StringBuffer的结果是一个字符串,不一定满足我们的要求,所以我们只能选择数组,即对象数组。

而对象数组又不能适应变化的需求,因为数组的长度是固定的,这个时候,为了适应变化的需求,Java提供了集合类

集合和数组的区别:

  • 数组虽然也可以存储对象,但是长度是固定的;集合长度是可变
  • 数组中可以存储基本数据类型,集合只能存储对象
  • 数组只能存储同一种类型;集合可以存储不同类型(一般也只存储同一种类型)

集合可以存储多个元素,但是存储多个元素我们也是有不同需求的:比如说这多个元素中不能有相同的元素,或者这多个元素按照某种规则排序。由于需求不同,Java就提供了不同的集合类。这多个集合类的数据结构不同,但是它们都是要提供存储和遍历功能的,我们把它们的共性不断的向上提取,最终就形成了集合的继承体系结构(下图为部分常用)。

Collection 层次结构中的根接口为Collection ,其表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。

2.2 集合Collection功能

其中的泛型(尖括号<>)都暂时约定为Object类,泛型后面学习。

首先来测试不带All的方法:

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

public class CollectionDemo {
    public static void main(String[] args) {
        // 测试不带All的方法
        // 创建集合对象
        // Collection c = new Collection(); //错误,因为接口不能实例化
        Collection c = new ArrayList(); //需要Collection接口的实现类ArrayList进行实例化(后面学习ArrayList)

        // boolean add(Object obj):添加一个元素
        // System.out.println("add:"+c.add("hello"));
        c.add("hello");
        c.add("world");
        c.add("java");

        // void clear():移除所有元素
        // c.clear();

        // boolean remove(Object o):移除一个元素
        // System.out.println("remove:" + c.remove("hello"));
        // System.out.println("remove:" + c.remove("javaee"));

        // boolean contains(Object o):判断集合中是否包含指定的元素
        // System.out.println("contains:"+c.contains("hello"));
        // System.out.println("contains:"+c.contains("android"));

        // boolean isEmpty():判断集合是否为空
        // System.out.println("isEmpty:"+c.isEmpty());

        //int size():元素的个数
        System.out.println("size:"+c.size());
        
        System.out.println("c:" + c);
    }
}

接着来测试带All的方法:

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

public class CollectionDemo2 {
    public static void main(String[] args) {
        // 创建集合1
        Collection c1 = new ArrayList();
        c1.add("abc1");
        c1.add("abc2");
        c1.add("abc3");
        c1.add("abc4");

        // 创建集合2
        Collection c2 = new ArrayList();
        // c2.add("abc1");
        // c2.add("abc2");
        // c2.add("abc3");
        // c2.add("abc4");
        c2.add("abc5");
        c2.add("abc6");
        c2.add("abc7");

        // boolean addAll(Collection c):添加一个集合的元素
        // System.out.println("addAll:" + c1.addAll(c2));
        
        //boolean removeAll(Collection c):移除一个集合的元素,只要有一个元素被移除了,就返回true。
        //System.out.println("removeAll:"+c1.removeAll(c2));

        //boolean containsAll(Collection c):判断集合中是否包含指定的集合元素,只有包含所有的元素,才叫包含
        // System.out.println("containsAll:"+c1.containsAll(c2));
        
        //boolean retainAll(Collection c)
        //假设有两个集合A,B。
        //A对B做交集,最终的结果保存在A中,B不变。
        //返回值表示的是A是否发生过改变。
        System.out.println("retainAll:"+c1.retainAll(c2));
        
        System.out.println("c1:" + c1);
        System.out.println("c2:" + c2);
    }
}

最后测试一下转换方法以及遍历:

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

public class CollectionDemo3 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();

        // 添加元素
        c.add("hello"); // Object obj = "hello"; 向上转型
        c.add("world");
        c.add("java");

        // 遍历
        // Object[] toArray():把集合转成数组,可以实现集合的遍历
        Object[] objs = c.toArray();
        for (int x = 0; x < objs.length; x++) {
            // 我知道元素是字符串,我在获取到元素的的同时,还想知道元素的长度。
            // System.out.println(objs[x] + "---" + objs[x].length());
            // 上面的实现不了,原因是Object中没有length()方法
            // 要想使用字符串的方法,就必须把元素还原成字符串
            // 向下转型
            String s = (String) objs[x];
            System.out.println(s + "---" + s.length());
        }
    }
}

补充:集合的toString()问题

Collection c = new ArrayList();
c.add("hello");
System.out.println(c);

为什么c输出的不是地址值呢?

public String toString() {
    Iterator<E> it = iterator(); //集合本身调用迭代器方法,得到集合迭代器
    if (! it.hasNext())
        return "[]";
    
    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = it.next(); //e=hello,world,java
        sb.append(e == this ? "(this Collection)" : e);
        if (! it.hasNext())
            //[hello, world, java]
            return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }
}

2.3 Collection集合遍历(使用迭代器)

除了将集合先转换为数组再对数组进行遍历,java还提供了迭代器来进行集合的遍历。迭代器是集合获取元素的方式,是依赖于集合而存在的一个接口(不是类)。

Iterator iterator():迭代器,集合的专用遍历方式

下面来看看迭代器的使用方式:

举个案例,创建五个学生,把他们存入到集合中并使用迭代器进行遍历。

public class Student {
    // 成员变量
    private String name;
    private int age;

    // 构造方法
    public Student() { super();}
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    // 成员方法
    // getXxx()/setXxx()
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorTest2 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();

        // 创建学生对象
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("风清扬", 30);
        Student s3 = new Student("令狐冲", 33);
        Student s4 = new Student("武鑫", 25);
        Student s5 = new Student("刘晓曲", 22);

        // 把学生添加到集合中
        c.add(s1);
        c.add(s2);
        c.add(s3);
        c.add(s4);
        c.add(s5);

        // 遍历
        Iterator it = c.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();
            System.out.println(s.getName() + "---" + s.getAge());

            // NoSuchElementException 不要多次使用it.next()方法
            // System.out.println(((Student) it.next()).getName() + "---"
            // + ((Student) it.next()).getAge());

        }
        // System.out.println("----------------------------------");

        // for循环改写
        // for(Iterator it = c.iterator();it.hasNext();){
        //     Student s = (Student) it.next();
        //     System.out.println(s.getName() + "---" + s.getAge());
        // }
    }
}

注意:不要多次使用 it.next() 方法,因为每次使用都是访问下一个对象。

迭代器为什么不定义成一个类,而是一个接口呢?

  • 假设迭代器定义的是一个类,这样我们就可以创建该类的对象,调用该类的方法来实现集合的遍历。但是,java中提供了很多的集合类,且这些集合类的数据结构是不同的,所以存储的方式和遍历的方式应该也是不同的。所以就没有定义迭代器类。
  • 而无论哪一种集合类,都应该具备获取元素的操作,并且最好在辅助于判断功能,这样就可以在获取前先判断,减少错误发生。即判断和获取应该是一个集合遍历所具备的,而每种集合的方式又不太一样,所以这两个功能被提取出来,并不提供具体实现,这种方式叫接口。
  • 而真正的具体实现方式在迭代器接口的实现类中以内部类的方式体现。

3 集合List

3.1 List接口概述

List是Collection接口的子接口,其特点是有序(存储顺序和取出顺序一致),可重复。

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

public class demo {
    public static void main(String[] args) {
        // 创建集合对象
        List list = new ArrayList();

        // 创建学生对象
        Student s1 = new Student("白骨精", 30);
        Student s2 = new Student("蜘蛛精", 40);
        Student s3 = new Student("观音姐姐", 22);
        Student s4 = new Student("蜘蛛精", 40);

        // 把学生对象添加到集合对象中
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);

        // 遍历
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}

/*
运行结果:
白骨精---30
蜘蛛精---40
观音姐姐---22
蜘蛛精---40
*/

3.2 List集合的特有功能

因为Collection集合的功能List集合都有,所以这里不做赘述,只学习List集合的特有功能。

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

public class ListDemo {
    public static void main(String[] args) {
        // 创建集合对象
        List list = new ArrayList();

        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        // void add(int index,Object element):在指定位置添加元素
        // list.add(1, "android");
        // list.add(11, "javaee");//报错 IndexOutOfBoundsException 不能在索引不可达的位置添加元素

        // Object get(int index):获取指定位置的元素
        // System.out.println("get:" + list.get(1));

        // Object remove(int index):根据索引删除元素,返回被删除的元素
        // System.out.println("remove:" + list.remove(1));

        // Object set(int index,Object element):根据索引修改元素,返回被修饰的元素
        System.out.println("set:" + list.set(1, "javaee"));

        System.out.println("list:" + list);
    }
}

利用List集合的size()和get()方法还可以对其进行遍历:

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

public class ListDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        List list = new ArrayList();

        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        for (int x = 0; x < list.size(); x++) {
            // System.out.println(list.get(x));

            String s = (String) list.get(x);
            System.out.println(s);
        }
    }
}

3.3 List集合的列表迭代器

ListIterator listIterator():List集合特有的迭代器(接口),其继承了Iterator接口,所以Iterator有的方法它也有(总的来说基本不使用)。

其特有功能:

利用这两个方法可以实现逆向遍历(必须先正向遍历)。

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

public class ListIteratorDemo {
    public static void main(String[] args) {
        // 创建List集合对象
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add("java");

        // ListIterator listIterator()
        ListIterator lit = list.listIterator(); // 返回的是子类对象
        while (lit.hasNext()) {
            String s = (String) lit.next();
            System.out.println(s);
        }
        System.out.println("-----------------");
        
        while (lit.hasPrevious()) {
            String s = (String) lit.previous();
            System.out.println(s);
        }
        System.out.println("-----------------");
    }
}

3.4 并发修改异常

问题引入:有一个集合如下,判断里面有没有"world"这个元素,如果有,就添加一个"javaee"元素。

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

public class demo {
    public static void main(String[] args) {
        // 创建List集合对象
        List list = new ArrayList();
        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        // 迭代器遍历
        Iterator it = list.iterator();
        while (it.hasNext()) {
            String s = (String) it.next();
            if ("world".equals(s)) {
                list.add("javaee");
            }
        }
        System.out.println("list:" + list);
    }
}

运行代码报错:ConcurrentModificationException。产生的原因是:

如何解决呢?

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

public class demo {
    public static void main(String[] args) {
        // 创建List集合对象
        List list = new ArrayList();
        // 添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        // 方式1:迭代器迭代元素,迭代器修改元素
        // 而Iterator迭代器却没有添加功能,所以我们使用其子接口ListIterator
       /* 
        ListIterator lit = list.listIterator();
        while (lit.hasNext()) {
            String s = (String) lit.next();
            if ("world".equals(s)) {
                lit.add("javaee");
            }
        }
        */
        // 方式2:集合遍历元素,集合修改元素(普通for)
        for (int x = 0; x < list.size(); x++) {
            String s = (String) list.get(x);
            if ("world".equals(s)) {
                list.add("javaee");
            }
        }
        System.out.println("list:" + list);
    }
}

3.5 常见的数据结构


4 List的子类

4.1 List的子类特点

4.2 ArrayList

List 接口的大小可变数组的实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)

其构造方法:

其存储和遍历如下:

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

public class ArrayListDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        ArrayList array = new ArrayList();

        // 创建学生对象
        Student s1 = new Student("武松", 30);
        Student s2 = new Student("鲁智深", 40);
        Student s3 = new Student("林冲", 36);
        Student s4 = new Student("杨志", 38);

        // 添加元素
        array.add(s1);
        array.add(s2);
        array.add(s3);
        array.add(s4);

        // 遍历
        Iterator it = array.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();
            System.out.println(s.getName() + "---" + s.getAge());
        }

        System.out.println("----------------");

        for (int x = 0; x < array.size(); x++) {
            Student s = (Student) array.get(x);
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}

4.3 Vector

Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

其构造方法:

Vector类特有的功能:

import java.util.Enumeration;
import java.util.Vector;

public class VectorDemo {
    public static void main(String[] args) {
        // 创建集合对象
        Vector v = new Vector();

        // 添加功能
        v.addElement("hello");
        v.addElement("world");
        v.addElement("java");

        // 遍历
        for (int x = 0; x < v.size(); x++) {
            String s = (String) v.elementAt(x);
            System.out.println(s);
        }

        System.out.println("------------------");

        Enumeration en = v.elements(); // 返回的是实现类的对象
        while (en.hasMoreElements()) {
            String s = (String) en.nextElement();
            System.out.println(s);
        }
    }
}

4.4 LinkedList

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

其构造方法:

LinkedList的特有功能:

import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        // 创建集合对象
        LinkedList link = new LinkedList();

        // 添加元素
        link.add("hello");
        link.add("world");
        link.add("java");

        // public void addFirst(Object e)
        // link.addFirst("javaee");
        // public void addLast(Object e)
        // link.addLast("android");

        // public Object getFirst()
        // System.out.println("getFirst:" + link.getFirst());
        // public Obejct getLast()
        // System.out.println("getLast:" + link.getLast());

        // public Object removeFirst()
        System.out.println("removeFirst:" + link.removeFirst());
        // public Object removeLast()
        System.out.println("removeLast:" + link.removeLast());

        // 输出对象名
        System.out.println("link:" + link);
    }
}

4.5 List案例

案例一:ArrayList去除集合中字符串的重复值(字符串的内容相同)。

分析:

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

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建集合对象
        ArrayList array = new ArrayList();

        // 添加多个字符串元素(包含内容相同的)
        array.add("hello");
        array.add("world");
        array.add("java");
        array.add("world");
        array.add("java");
        array.add("world");
        array.add("java");
        array.add("world");

        // 创建新集合
        ArrayList newArray = new ArrayList();

        // 遍历旧集合,获取得到每一个元素
        Iterator it = array.iterator();
        while (it.hasNext()) {
            String s = (String) it.next();
            // 拿这个元素到新集合去找,看有没有
            if (!newArray.contains(s)) {
                newArray.add(s);
            }
        }

        // 遍历新集合
        for (int x = 0; x < newArray.size(); x++) {
            String s = (String) newArray.get(x);
            System.out.println(s);
        }
    }
}

注意:如果元素是对象,则需看需求重写自定义类的equals()方法。因为contains()方法的底层依赖的是equals()方法。而学生类中没有equals()方法,这个时候,默认使用的是它父亲Object的equals()方法,Object()的equals()默认比较的是地址值。因为new的对象,地址值都不同,所以每次 if 判断都会为true。

案例二:与案例一相同,增加条件,不能创建新的集合。

分析:引入选择排序思想,拿0索引的依次和后面的比较,有就把后面的remove。同理,拿1索引依次去和后面的比较,以此类推,完成去重(注意:remove元素之后记得-1)。

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

public class ArrayListDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        ArrayList array = new ArrayList();

        // 添加多个字符串元素(包含内容相同的)
        array.add("hello");
        array.add("world");
        array.add("java");
        array.add("world");
        array.add("java");
        array.add("world");

        // 选择排序思想
        for (int x = 0; x < array.size() - 1; x++) {
            for (int y = x + 1; y < array.size(); y++) {
                if (array.get(x).equals(array.get(y))) {
                    array.remove(y);
                    y--;
                }
            }
        }

        // 遍历集合
        Iterator it = array.iterator();
        while (it.hasNext()) {
            String s = (String) it.next();
            System.out.println(s);
        }
    }
}

案例三:用LinkedList模拟一个栈数据结构的集合类,并测试。

分析:自己的定义一个集合类,在这个集合类内部可以使用LinkedList模拟。(并不是用LinkedList的特有添加功能addFirst()方法)

//MyStack.java 自定义类
import java.util.LinkedList;

public class MyStack {
    private LinkedList link;

    public MyStack() { link = new LinkedList(); }

    public void add(Object obj) { link.addFirst(obj); }
    public Object get() { return link.removeFirst(); }
    public boolean isEmpty() { return link.isEmpty(); }
}
//MyStackDemo.java 自定义类测试类
public class MyStackDemo {
    public static void main(String[] args) {
        // 创建集合对象
        MyStack ms = new MyStack();

        // 添加元素
        ms.add("hello");
        ms.add("world");
        ms.add("java");

        // System.out.println(ms.get());
        // System.out.println(ms.get());
        // System.out.println(ms.get());
        
        while(!ms.isEmpty()){
            System.out.println(ms.get());
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读