Java集合框架之Collection接口

时光毁灭记忆、已成空白 提交于 2020-01-27 14:20:25

在Java中,集合框架里所提到的类集实际上就是动态对象数组,因为数组本身有一个缺陷,就是长度固定。为了解决这个问题,Java里提供了动态的对象数组实现框架—Java类集框架。而在Java类集里面,提供了两个最核心的接口:Collection和Map接口。今天我来总结一下Collection接口。首先看一张图:
在这里插入图片描述
这是关于类集里面Collection接口的结构图,包含了子接口以及它的实现类。

Collection接口

Collection接口有两个重要的方法:
1)add()
public boolean add(E e) 向集合中添加数据
2)iterator()
public Iterator iterator() 取得Iterator接口对象,用于集合输出

1、List接口

List接口是Collection接口的子接口,除了包含Collection接口的那两个方法外,还扩充了以下两个方法:
1)get()
public E get(int index) 根据索引取得保存的数据
2)set()
public E set(int index,E element) 修改数据

List是一个接口,要想取得实例化对象,就必须有子类,在List接口下,有三个常用的子类:ArrayList、Vector、LinkedList。

1.1 ArrayList子类(优先考虑)

1.1.1 List的基本处理:

public class TestArrayList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nanfeng");
        System.out.println(list);
    }
}

输出结果为:
在这里插入图片描述
可以看出,List允许保存重复的数据

1.1.2 如果调用remove()方法,只会删除其中的一个“Hello”:

public class TestArrayList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nanfeng");
        System.out.println(list.size()+"、"+list.isEmpty());
        System.out.println(list);
        System.out.println(list.remove("Hello"));
        System.out.println(list);
    }
}

1.1.3 List的get()方法

public class TestArrayList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nanfeng");
        for (int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}

在这里插入图片描述
在这里插入图片描述
1.1.5 集合操作简单Java类的时候,对于remove()、contains()方法需要覆写Objects类的equals()方法。

1.2 Vector子类(使用较少)

public class TestArrayList {
    public static void main(String[] args) {
        List<String> list = new Vector<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nanfeng");
        System.out.println(list);
        list.remove("Hello");
        System.out.println(list);
    }
}

在这里插入图片描述
面试题:请解释ArrayList与Vector的区别:
1)ArrayList是JDK1.2提供的,而Vector是JDK1.0提供的
2)ArrayList是异步处理,性能更高,而Vector是同步处理,性能较低
3)ArrayList是线程不安全的,而Vector是性能安全的
4)ArrayList支持Iterator、ListIterator、foreach,而Vector除了支持这三个,还支持Enumeration

1.3 LinkedList子类

public class TestArrayList {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nanfeng");
        System.out.println(list);
        list.remove("Hello");
        System.out.println(list);
    }
}

在这里插入图片描述
这个子类如果向父接口转型的话,使用形式和之前没有任何区别。

ArrayList、Vector、LinkedList的关系与区别:

  • 以上三个类都是List接口下的常用子类,其中ArrayList与Vector基于数组实现,LinkedList基于双向链表实现。
  • ArrayList采用懒加载策略(第一次add时才初始化内部数组,默认初始化大小为10),扩容为原先数组的1.5倍,采用异步处理,线程不安全,性能较高。
  • ArrayList在大部分场合(80%,频繁查找,在集合末端插入与删除)都采用ArrayList
  • Vector在产生对象时就初始化内部数组为10,扩容为原先数组的2倍。采用Synchronized修饰增删查改方法,线程安全,性能较低(锁的力度太粗,将当前集合对象锁住,读读都互斥)。Java中JDK内置的Stack是Vector子类。
  • LinkedList采用异步处理,线程不安全。频繁在任意位置的插入与删除考虑使用。LinkedList是Queue接口常用子类。

2、Set接口

2.1 HashSet的使用

public class TestSet {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Hello");
        set.add("Hello");
        set.add("Nanfeng");
        set.add("Hello");
        System.out.println(set);
    }
}

在这里插入图片描述
2.2 TreeSet的使用

public class TestSet {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("C");
        set.add("C");
        set.add("D");
        set.add("B");
        set.add("A");
        System.out.println(set);
    }
}

在这里插入图片描述
2.2.1 TreeSet排序分析

如果要进行对象数组的排序,对象所在的类一定要实现Comparable接口并且覆写compareTo()方法,只有通过此方法才能知道大小关系。

  • HashSet:无序存储
  • TreeSet:有序存储
  • 序指的是存储该类的大小关系(内部或外部排序返回的大小关系),与插入顺序无关。

自定义类要想存储在TreeSet中,要么该类实现了内部排序(如String或Integer),要么通过构造方法传入该类的比较器(实现了外部排序Comparator接口)。

class Person2 {
    private Integer age;
    private String name;

    public Person2(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//年龄升序比较器
class AgeSec implements Comparator<Person2> {

    @Override
    public int compare(Person2 o1, Person2 o2) {
        if (o1.getAge() < o2.getAge())
            return -1;
        else if (o1.getAge() > o2.getAge())
            return 1;
        else
            return 0;
    }
}

//年龄降序比较器
class AgeDesc implements Comparator<Person2> {
    @Override
    public int compare(Person2 o1, Person2 o2) {
        if (o1.getAge() < o2.getAge())
            return 1;
        else if (o1.getAge() > o2.getAge())
            return -1;
        else
            return 0;
    }
}

public class Test {
    public static void main(String[] args) {
        AgeSec ageSec = new AgeSec();
        Set<Person2> set = new TreeSet<>(ageSec);
        Person2 per1 = new Person2(20, "张三");
        Person2 per2 = new Person2(21, "张三");
        set.add(per1);
        set.add(per2);
        System.out.println(set);
    }
}

在这里插入图片描述
如果这个类既传入了比较器,又天然可比较呢?这时TreeSet到底是按照什么排序呢?看例子:

class Person2 implements Comparable<Person2> {
    private Integer age;
    private String name;

    public Person2(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person2{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }


    @Override
    public int compareTo(Person2 o) {
        if (this.age < o.age)
            return 1;
        else if (this.age > o.age)
            return -1;
        return 0;
    }
}

//年龄升序比较器
class AgeSec implements Comparator<Person2> {

    @Override
    public int compare(Person2 o1, Person2 o2) {
        if (o1.getAge() < o2.getAge())
            return -1;
        else if (o1.getAge() > o2.getAge())
            return 1;
        else
            return 0;
    }
}

//年龄降序比较器
class AgeDesc implements Comparator<Person2> {
    @Override
    public int compare(Person2 o1, Person2 o2) {
        if (o1.getAge() < o2.getAge())
            return 1;
        else if (o1.getAge() > o2.getAge())
            return -1;
        else
            return 0;
    }
}

public class Test {
    public static void main(String[] args) {
        AgeSec ageSec = new AgeSec();
        Set<Person2> set = new TreeSet<>(ageSec);
        Person2 per1 = new Person2(20, "张三");
        Person2 per2 = new Person2(21, "张三");
        set.add(per1);
        set.add(per2);
        System.out.println(set);
    }
}

在这里插入图片描述
由结果来看:TreeSet优先使用外部比较器。(由compare里定义的三目运算决定)

2.2.2 重复元素判断
 在使用TreeSet子类保存数据时,重复元素的判断依靠Comparable接口实现;如果使用HashSet子类保存数据,重复元素的判断要依靠Object类中的两个方法:
 1)hash码:public native int hashCode();
 2)对象比较:pubic boolean equals(Object obj);
 代码示例:

class Person implements Comparable<Person>{

    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ",age=" + age +"}";
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj)
            return true;
        if (obj==null || getClass()!= obj.getClass())
            return false;
        Person person = (Person) obj;
        return Objects.equals(name,person.name) && Objects.equals(age,person.age);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name,age);
    }

    @Override
    public int compareTo(Person o) {
        if (this.age>o.age){
            return 1;
        }else if(this.age<o.age){
            return -1;
        }else {
            return this.name.compareTo(o.name);
        }
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

public class TestSet {
    public static void main(String[] args) {
        Set<Person> set = new HashSet<>();
        set.add(new Person("张三",20));
        set.add(new Person("张三",20));
        set.add(new Person("李四",20));
        set.add(new Person("王五",19));
        System.out.println(set);
    }
}

在这里插入图片描述
如果要想标识对象的唯一性,一定要equals()与hashCode()方法共同调用。
面试题:
1)如果两个对象的hashCode()相同,equals()不同结果是什么?
  不能消除
2)如果两个对象的hashCode()不同,equals()相同结果是什么?
  不能消除
个人建议:
保存自定义对象的时候使用List接口,保存系统类信息的时候使用Set接口(避免重复)。

3、集合输出

集合输出除了之前使用的toString()方法,List接口中的get()方法,还有四种标准方法:
Iterator、ListIterator、Enumeration、foreach

3.1 迭代输出:Iterator(重要)— 集合输出就用Iterator

Collection接口中的iterator()方法可以取得Iterator接口的实例化对象。
Iterator接口里含有三个抽象方法:
1)判断是否有下一个元素:public boolean hasNext();
2)取得当前元素:public E next();
3)删除元素:public default void remove();

3.1.1 Iterator的使用

public class TestIterator {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nanfeng");
        Iterator<String> iterator = list.iterator();    //实例化Iterator对象
        while (iterator.hasNext()){
            String str = iterator.next();
            System.out.println(str);
        }
    }
}

在这里插入图片描述
集合输出的时候不要修改集合中元素

3.1.2 删除元素

public class TestIterator {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nan");
        list.add("Nanfeng");
        list.add("Nanfeng");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String str = iterator.next();
            if(str.equals("Nan")){
                iterator.remove();		//如果使用的是集合里面的remove()方法,会产生ConcurrentModificationException
                continue;
            }
            System.out.println(str);
        }
    }
}

在这里插入图片描述

3.2 双向迭代接口:ListIterator

Iterator输出只能由前向后进行内容的迭代处理,如果要进行双向迭代,就必须依靠Iterator的子接口:ListIterator来实现。
ListIterator接口定义了两个方法:
1)判断是否有上一个元素:public boolean hasPrevious();
2)取得上一个元素:public E previous();
注意:Iterator接口对象是由Collection接口支持的,但是ListIterator是由List接口支持的
List接口提供如下方法:
3)取得ListIterator接口对象:public ListIterator listIterator();

3.2.1 ListIterator接口的使用

public class TestListIterator {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nan");
        list.add("Nanfeng");
        ListIterator<String> listIterator = list.listIterator();
        System.out.println("从前往后输出:");
        while (listIterator.hasNext()){
            System.out.print(listIterator.next() + "、");
        }
        System.out.println("\n从后往前输出:");
        while (listIterator.hasPrevious()){
            System.out.print(listIterator.previous()+"、");
        }
    }
}

在这里插入图片描述
注意:如果要实现从后往前输出,那么必须先从前往后输出,否则无法实现双向。

3.3 Enumeration枚举输出
Enumeration的接口定义:
1)判断是否有下一个元素:public boolean hasMoreElements();
2)取得元素:public E nextElement();
要想取得这个接口的实例化对象,只能依靠Vector子类。
3)取得Enumeration接口对象:public Enumeration elements()

3.3.1 使用Enumeration输出

public class TestEnumeration {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("Hello");
        vector.add("Hello");
        vector.add("Nan");
        vector.add("Nanfeng");
        Enumeration<String> enumeration = vector.elements();
        while (enumeration.hasMoreElements()){
            System.out.println(enumeration.nextElement());
        }
    }
}

在这里插入图片描述
3.4 foreach输出
JDK1.5开始,foreach可以输出数组,其实也可以输出集合。

3.4.1 使用foreach输出

public class Testfroeach {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("Hello");
        list.add("Nan");
        list.add("Nanfeng");
        for (String str:list){
            System.out.println(str);
        }
    }
}

在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!