zoukankan      html  css  js  c++  java
  • Java基础知识:Collection接口

    *本文是最近学习到的知识的记录以及分享,算不上原创。

    *参考文献见文末。

    这篇文章主要讲的是java的Collection接口派生的两个子接口List和Set。

    目录

      Collection框架

      List接口

      Set接口

    1.Collection框架

    首先我们综合性地看一下java的Collection接口的框架,如下图:

      *图中绿色表示接口,白色表示类。

    List接口和Set接口是Collection接口派生的主要的两个子接口。

    2.List接口

    List的主要特征

    (1)有序(ordered):元素的存取是有序的,保证了取出的元素的顺序与输入的元素顺序保持一致。

    例如:

     1 import java.util.ArrayList;
     2 import java.util.LinkedList;
     3 import java.util.Vector;
     4 
     5 public class CollectionDemo {
     6     public static void main(String[] args) {
     7         //ArrayList
     8         ArrayList<String> list=new ArrayList<String>();
     9         list.add("11");
    10         list.add("22");
    11         list.add("33");
    12         for(int i=0;i<list.size();++i){
    13             System.out.print(list.get(i)+"  "); //output: 11  22  33  
    14         }
    15         System.out.println("");
    16         //LinkedList
    17         LinkedList<String> list2=new LinkedList<String>();
    18         list2.add("11");
    19         list2.add("22");
    20         list2.add("33");
    21         for(int i=0;i<list2.size();++i){
    22             System.out.print(list.get(i)+"  ");  //output: 11  22  33  
    23         }
    24         System.out.println("");
    25         //Vector
    26         Vector<String> list3=new Vector<String>();
    27         list3.add("11");
    28         list3.add("22");
    29         list3.add("33");
    30         for(int i=0;i<list3.size();++i){
    31             System.out.print(list.get(i)+"  ");  //output: 11  22  33  
    32         }
    33         System.out.println("");
    34     }
    35 }
    List元素存取有序

    (2)索引(index):允许用户根据索引对元素进行精准定位并进行查询、插入、删除等操作。

    *所以,对List的遍历,不仅可以通过Iterator,还可以通过索引(index)。

    (3)允许重复:允许多个重复的元素存在。

    List的主要方法

    https://docs.oracle.com/javase/7/docs/api/java/util/List.html 

    在提到List接口的各种实现类之前,首先我们回顾一下数据结构中数组和链表的各自的特色。

    数组易于对元素的查询、遍历,但对元素的增删操作比较繁琐;链表能够方便地对元素进行增删,但不利于元素的查询、遍历。

    2.1 ArrayList

    http://www.cnblogs.com/skywang12345/p/3308556.html

    ArrayList元素存储的数据结构是数组结构。ArrayList相当于动态数组,既保持了数组查询快速的优点,又不像数组那样对元素的增删慢,所以是最常用的集合。

    ArrayList的Clone()

    ArrayList的clone()属于浅拷贝

    浅拷贝与深拷贝的问题,这与引用对象的存储方式有关系。浅拷贝简单地说,就是把复制一个指向该对象的箭头给你,深拷贝简单地说,就是复制一个对象给你。

    当ArrayList中的元素为基本数据类型时,可以说不存在浅拷贝与深拷贝的问题。

    例如:

    import java.util.ArrayList;
    
    public class ListDemo3 {
        public static void main(String[] args) {
            ArrayList<Integer> a1=new ArrayList<Integer>();
            a1.add(1);
            a1.add(2);
            a1.add(3);
            //基本数据类型: byte short int long float double boolean char
            ArrayList<Integer> a2=(ArrayList<Integer>) a1.clone();
            System.out.println(a1); //[1, 2, 3]
            System.out.println(a2); //[1, 2, 3]
            a1.set(0, 10);
            a2.remove(2);
            System.out.println(a1); //[10, 2, 3]
            System.out.println(a2); //[1, 2]
        }
    }
    View Code

    当ArrayList中的元素为引用数据类型时,要意识到浅拷贝与深拷贝的问题。

    例如:

    import java.util.ArrayList;
    
    public class ListDemo3 {
        public static void main(String[] args) {
            ArrayList<Student> b1=new ArrayList<Student>();
            Student s1=new Student(001,"zhangsan",22);
            Student s2=new Student(002,"lisi",21);
            Student s3=new Student(003,"wangwu",18);
            b1.add(s1);
            b1.add(s2);
            b1.add(s3);
            //基本数据类型: byte short int long float double boolean char
            //引用数据类型:接口interface, 类class, 数组
            ArrayList<Student> b2=(ArrayList<Student>) b1.clone();
            System.out.println(b1); 
            System.out.println(b2);
            /*
             * [Student [id=1, name=zhangsan, age=22], Student [id=2, name=lisi, age=21], Student [id=3, name=wangwu, age=18]]
             * [Student [id=1, name=zhangsan, age=22], Student [id=2, name=lisi, age=21], Student [id=3, name=wangwu, age=18]]
             */
            b2.remove(2);
            System.out.println(b1);
            System.out.println(b2);
            /*
             * [Student [id=1, name=zhangsan, age=22], Student [id=2, name=lisi, age=21], Student [id=3, name=wangwu, age=18]]
             * [Student [id=1, name=zhangsan, age=22], Student [id=2, name=lisi, age=21]]
             */
            b1.get(0).setName("wangmazi");
            System.out.println(b1);
            System.out.println(b2);
            /*
             * [Student [id=1, name=wangmazi, age=22], Student [id=2, name=lisi, age=21], Student [id=3, name=wangwu, age=18]]
             * [Student [id=1, name=wangmazi, age=22], Student [id=2, name=lisi, age=21]]
             */
        }
    }

    第一步:创建ArrayList对象b1

     第二步:clone()

    从下图中,可以看到左边的箭头和右边的箭头,箭头左边是地址,箭头右边是指向的对象。我们可以发现虽然b1和b2的地址不同,但指向相同的对象。如果仅仅改变左边的箭头,如改变b1的箭头则不会影响到b2,但如果改变了右边的箭头,如改变了s1的箭头,就会同时对b1, b2造成影响。

    Arraylist的遍历

    ArrayList有三种遍历方式。

    例如:

    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class ListDemo2 {
        public static void main(String[] args) {
            ArrayList<String> list=new ArrayList<String>();
            list.add("11");
            list.add("22");
            list.add("33");
            //第一种遍历方式:Iterator
            Iterator<String> it=list.iterator();
            while(it.hasNext()){
                System.out.print(it.next()+" "); //11 22 33
            }
            System.out.println("");
            //第二种遍历方式
            for(int i=0;i<list.size();++i){
                System.out.print(list.get(i)+" "); //11 22 33
            }
            System.out.println("");
            //第三种遍历方式
            for(String s:list){
                System.out.print(s+" "); //11 22 33
            }
            System.out.println("");
        }
    }

    结果显示,这三种遍历方法中,第二种(使用索引index)的效率最高,第一种(使用Iterator)的效率最低。

    ArrayList的toArray(T[] contents)

    Arraylist提供了两个将ArrayList转换为数组的方法:

    Object[] toArray()
    <T> T[] toArray(T[] contents)

    由于toArray()返回的类型是Object[],如果进行强制类型转换会造成java.lang.ClassCastException。因此调用toArray()容易出错,更建议使用toArray(T[] contents)。

    例如:

    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class ListDemo2 {
        public static void main(String[] args) {
            ArrayList<String> list=new ArrayList<String>();
            list.add("11");
            list.add("22");
            list.add("33");
            //方法1
            String[] str=new String[list.size()];
            str=list.toArray(str);
            System.out.println(Arrays.toString(str)); //[11, 22, 33]
            //方法2
            String[] str2=(String[])list.toArray(new String[0]);
            System.out.println(Arrays.toString(str2)); //[11, 22, 33]
        }
    }

    2.2 LinkedList

    LinkedList元素存储的数据结构是链表结构。LinkedList能够方便地对元素进行增删。

    LinkedList提供了一些方法,来方便对首尾元素的操作。

    LinkedList还可以作为堆栈、队列的结构使用,所以提供了一些和堆栈、队列相关的方法。

     例如:

     1 import java.util.LinkedList;
     2 public class ListDemo1 {
     3     public static void main(String[] args) {
     4         LinkedList<String> list=new LinkedList<String>();
     5         list.add("11");
     6         list.add("22");
     7         list.add("33");
     8         while(!list.isEmpty()){
     9             System.out.print(list.pop()+" "); //output: 11 22 33
    10         }
    11         System.out.println("");
    12     }
    13 }
    LinkedList的pop()

    *注意元素存取的顺序,保持着先进先出的顺序。

    2.3 Vector

     Vector元素存储的数据结构是数组结构。Vector与ArrayList类似,Vector提供的Enumeration与ArrayList提供的Iterator类似,二者在功能上可以说的上是重复的。

    例如:

     1 import java.util.ArrayList;
     2 import java.util.Enumeration;
     3 import java.util.Iterator;
     4 import java.util.Vector;
     5 
     6 public class ListDemo1 {
     7     public static void main(String[] args) {
     8         //ArrayList和Iterator
     9         ArrayList<String> list=new ArrayList<String>();
    10         list.add("11");
    11         list.add("22");
    12         list.add("33");
    13         Iterator<String> it=list.iterator();
    14         while(it.hasNext()){
    15             System.out.print(it.next()+" ");
    16         }
    17         System.out.println("");
    18         //Vector和Enumeration
    19         Vector<String> list2=new Vector<String>();
    20         list2.add("11");
    21         list2.add("22");
    22         list2.add("33");
    23         Enumeration<String> en=list2.elements();
    24         while(en.hasMoreElements()){
    25             System.out.print(en.nextElement()+" ");
    26         }
    27         System.out.println("");
    28     }
    29 }
    ArrayList和Iterator vs.Vector和Enumration

    3.Set接口

    Set的主要特征

    (1)不允许重复:元素不允许重复。Set在存储元素时会通过hashCode()和equals()来保证元素的唯一性。

    Set如何保证元素的唯一性

    Set在存储元素时,通过hashCode()和equals()来保证元素的唯一性。

    事实上,当存储一个新的元素时,仅仅通过equals()来逐一判断新元素是否与集合中已有的元素是否重合,这种方法也是可行的,那为什么还需要hashCode()呢。因为当Set中元素数量很多时,通过equals()逐一判断并不是一个高效率的方法,所以同时通过hashCode()和equals()进行判断可以提高判断的效率。

    首先,我们回忆一下什么是hashcode。

    https://www.cnblogs.com/dolphin0520/p/3681042.html

    hashCode()是Object的类,每个对象都具有hashCode值。不同的对象可能会有相同的hashCode值,但hashCode值不相同的两个对象肯定不同。

    我们可以用映射的概念来理解对象与hashCode之间的关系,对象(value)与hashCode(key)构成了多对一的映射。

    当每次存储新的元素时,首先通过hashCode()获得新元素的hashCode,判断是否与已有元素的hashCode相同。如果没有,将新元素加入到集合中。如果有,再通过equals()判断元素是否相同,如果相同,则不添加该元素,如果不同,则把该元素加到集合中。

    3.1 HashSet

    HashSet元素存储的结构是哈希表

    hashSet除了不允许重复元素外,还不能保证元素存取的顺序。

    例如:

     1 import java.util.HashSet;
     2 import java.util.Iterator;
     3 
     4 public class setDemo1 {
     5     public static void main(String[] args) {
     6         HashSet<String> hash=new HashSet<String>();
     7         hash.add("11");
     8         hash.add("22");
     9         hash.add("33");
    10         Iterator<String> it=hash.iterator();
    11         while(it.hasNext()){
    12             System.out.print(it.next()+" ");
    13         }
    14         System.out.println(""); //output: 22 33 11
    15     }    
    16 }
    不能保证存取顺序

    hashCode()和equals()的重写

    就像我们刚才提到的,hashSet是通过hashCode()和equals()来保证元素的唯一性。JaveAPI中的每个类(如String, Integer类)都能获得hashCode和equals的比较方法,所以这类元素可以直接用hashSet存储,但是用户自定义的类,也需要先重写hashCode()和equals(),之后才能用hashSet存储该类元素。

    *Object的equals()与==功能相同,判断的是地址是否相同。所以想要判断内容是否相同,必须要重写equals(),比如String类。

    例如:

    //用户自定义类
    public class Student {
        private int id;
        private String name;
        private int age;
        //构造方法
        public Student() {
            // TODO Auto-generated constructor stub
        }
        public Student(int id,String name,int age){
            this.id=id;
            this.name=name;
            this.age=age;
        }
        //getter and setter
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        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;
        }
        //overwrite hashCode() and equals()
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + id;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Student other = (Student) obj;
            if (age != other.age)
                return false;
            if (id != other.id)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
        //overwrite toString()
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
        }
    }
    
    //hashSet分别存储JavaAPI类和用户自定义类
    import java.util.HashSet;
    import java.util.Iterator;
    
    public class setDemo1 {
        public static void main(String[] args) {
            //用HashSet存储JavaAPI类,如String、Integer等
            HashSet<String> hash=new HashSet<String>();
            hash.add("11");
            hash.add("22");
            hash.add("33");
            hash.add("22");
            Iterator<String> it=hash.iterator();
            while(it.hasNext()){
                System.out.print(it.next()+" ");
            }
            /*
             * 不允许重复元素
             * 不保证存取顺序
             */
            System.out.println(""); //output: 22 33 11
            //用HashSet存储用户自定义类
            //首先在用户自定义类中需要重写hashCode()和equals()
            HashSet<Student> hash2=new HashSet<Student>();
            Student s1=new Student(001,"zhangsan",22);
            Student s2=new Student(002,"lisi",21);
            Student s3=new Student(003,"wangwu",18);
            hash2.add(s1);
            hash2.add(s2);
            hash2.add(s3);
            hash2.add(s1);
            Iterator<Student> it2=hash2.iterator();
            while(it2.hasNext()){
                System.out.println(it2.next()+" ");
            }
            /*
             * output:
             *     Student [id=3, name=wangwu, age=18] 
             *  Student [id=1, name=zhangsan, age=22] 
             *  Student [id=2, name=lisi, age=21] 
             */
        }    
    }
    View Code

    3.2 LinkedHashSet

    LinkedHashSet的元素的存储结构是链表和哈希表

    LinkedHashSet保证了元素存取的顺序。

    *LinkedHashSet遵循先进先出的顺序

    例如:

     1 import java.util.Iterator;
     2 import java.util.LinkedHashSet;
     3 
     4 public class setDemo2 {
     5     public static void main(String[] args) {
     6         LinkedHashSet<String> lset=new LinkedHashSet<String>();
     7         lset.add("11");
     8         lset.add("22");
     9         lset.add("33");
    10         Iterator<String> it=lset.iterator();
    11         while(it.hasNext()){
    12             System.out.print(it.next()+" ");
    13         }
    14         System.out.println(""); //output: 11 22 33
    15     }
    16 }
    View Code

    3.3 TreeSet

    TreeSet可以保证元素存取的顺序。

     参考文献

    https://docs.oracle.com/javase/7/docs/api/java/util/List.html

    https://www.cnblogs.com/dolphin0520/p/3681042.html

    https://www.cnblogs.com/jmsjh/p/7740123.html

  • 相关阅读:
    operator[],识别读操作和写操作
    COW写时复制
    嵌套类,PIMPL
    类型转换
    String类运算符重载,自己实现
    socket的几个配置函数
    TCP三次握手,四次挥手,状态变迁图
    运算符重载
    友元
    P4016 负载平衡问题(最小费用最大流)
  • 原文地址:https://www.cnblogs.com/liuyingsme/p/9845889.html
Copyright © 2011-2022 走看看