Java中的常用集合

来源:互联网 发布:db2创建序列的sql 编辑:程序博客网 时间:2024/06/05 00:35

为什么要使用集合类

当你事先不知道要存放数据的个数,或者你需要一种比数组下标存取机制更灵活的方法时,你就需要用到集合类。

理解集合类

集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。

  • 列表

列表的主要特征是其对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。
列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。
关于实现列表的集合类,是我们日常工作中经常用到的,将在后边的笔记详细介绍。

集(set)是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象。
集也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。它实现的是SortedSet接口,也就是加入了对象比较的方法。通过对集中的对象迭代,我们可以得到一个升序的对象集合。

  • 映射

映射与集或列表有明显区别,映射中每个项都是成对的。映射中存储的每个对象都有一个相关的关键字(Key)对象,关键字决定了对象在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值,散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。理想情况下,散列处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码。

集合类简介

Collction体系结构图

  • Collection

        List        ArrayList        Vector        LinkedList    Set        HashSet        TreeSet

    java.util中共有13个类可用于管理集合对象,它们支持集、列表或映射等集合,以下是这些类的简单介绍

  • Collection

    通过代码进行讲解:

public class CollectionDemo {    public static void main(String[] args) {        //创建集合        //Collection c = new Collection();        Collection<Comparable> c = new ArrayList<Comparable>();        //boolean add(Object obj):往集合中添加一个元素        c.add("hello");        c.add("world");        c.add("java");        c.add(100);//集合中只能添加引用类型的数据,但是在这里为什么可以添加int类型的数据100呢?在这里进行了自动装箱        //boolean addAll(Collection c):往集合中添加多个元素        Collection<String> c1 = new ArrayList<String>();        c1.add("章子怡");        c1.add("郭德纲");        c.addAll(c1);        //void clear():清空所有元素        //c.clear();        //boolean remove(Object o):从集合中删除一个元素//      boolean remove = c.remove("sfdgdfg");//      System.out.println(remove);        //boolean removeAll(Collection c):从集合中删除另一个集合的元素,只要删除一个元素就代表删除成功//      Collection<String> c2 = new ArrayList<String>();//      c2.add("hello");//      c2.add("sdfdsf");//      boolean removeAll = c.removeAll(c2);//      System.out.println(removeAll);        //boolean contains(Object o):判断集合中是否包含指定的元素//      boolean contains = c.contains("sdfcds");//      System.out.println(contains);        //boolean containsAll(Collection c):判断集合中是否包含另一个集合的元素,包含的所有元素才叫全部包含//      Collection<String> c2 = new ArrayList<String>();//      c2.add("hello");//      c2.add("sfsdfsdf");//      boolean containsAll = c.containsAll(c2);//      System.out.println(containsAll);        //boolean isEmpty():判断集合是否为空。//      c.clear();//      boolean empty = c.isEmpty();//      System.out.println(empty);        //boolean retainAll(Collection c):求出交集元素,并且将没有交集的元素删除//      Collection c2 = new ArrayList();//      c2.add("hello");//      c2.add("sfsdfsdf");//      boolean retainAll = c.retainAll(c2);//      System.out.println(retainAll);        //int size():返回集合中元素的个数        System.out.println(c.size());        System.out.println("---------------");        //怎么遍历集合呢?        //先将集合转成数组        Object[] objects = c.toArray();        for (int i = 0; i < objects.length; i++) {            System.out.println(objects[i]);        }    }}
  • 列表(List):继承自Collection;除上述方法外还特有功能:
  • List的倒序与洗牌
    List中元素顺序可以被洗牌Collections.shuffle(list)
    List中元素顺序可以被倒序Collections.reverse(list)
    Collections.sort(list)对List元素排序(字母数字分别进行测试)
  • List的特有功能:
    A:添加功能
    void add(int index,Object obj):在指定的位置添加元素
    B:删除功能
    Object remove(int index):通过指定的索引删除元素,并把删除的元素返回
    C:获取功能
    get(int index) 返回列表中指定位置的元素。
    D:替换功能
    Object set(int index,Object obj)

    -List的实现类:
    Vector: 实现一个类似数组一样的表,自动增加容量来容纳你所需的元素。使用下标存储和检索对象就象在一个标准的数组中一样。你也可以用一个迭代器从一个Vector中检索对象。Vector是唯一的同步容器类??当两个或多个线程同时访问时也是性能良好的。
    Stsck: 这个类从Vector派生而来,并且增加了方法实现栈??一种后进先出的存储结构。
    LinkedList: 实现一个链表。由这个类定义的链表也可以像栈或队列一样被使用。
    ArrayList: 实现一个数组,它的规模可变并且能像链表一样被访问。它提供的功能类似Vector类但不同步。

//List集合的其余遍历方式public class Student {    private String name;    private int age;    public Student(String name, int age) {        super();        this.name = name;        this.age = age;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    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;    }}import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class ListDemo {    public static void main(String[] args) {        /**         * B:List存储自定义对象并遍历(迭代器,普通for)         */        //创建学生对象        Student s1 = new Student("刘德华", 50);        Student s2 = new Student("张学友", 55);        Student s3 = new Student("黎明", 56);        Student s4 = new Student("郭富城", 60);        //创建List集合        List list = new ArrayList();        //给集合中添加元素        list.add(s1);        list.add(s2);        list.add(s3);        list.add(s4);        //普通for循环进行遍历        for(int i=0;i<list.size();i++){            Object object = list.get(i);            Student s = (Student)object;            System.out.println(s.getAge()+" "+s.getName());        }        System.out.println("-----------");        //获取迭代器对象        Iterator it = list.iterator();        while (it.hasNext()) {            Object obj = it.next();            Student s = (Student)obj;            System.out.println(s.getAge()+"  "+s.getName());        }        System.out.println("------------");        //使用增强for循环遍历集合        for(Object obj:list){            Student s= (Student)obj;            System.out.println(s.getAge()+"  "+s.getName());        }    }}
  • 集:

HashSet: 使用HashMap的一个集的实现。虽然集定义成无序,但必须存在某种方法能相当高效地找到一个对象。使用一个HashMap对象实现集的存储和检索操作是在固定时间内实现的. (元素唯一且无序,它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变)
为什么HashSet能保证元素的唯一性(分析源码)?
* 通过简单的分析,我们知道HashSet集合保证元素的唯一性和add()方法相关。
* 如何我们想深入的了解,就必须看add()方法的源码,看它的底层依赖什么内容?
* if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {…}
*
* 左边:e.hash == hash
* 比较对象的哈希值。
*
* 右边:((k = e.key) == key || key.equals(k))
* 左边:(k = e.key) == key
* 比较对象的地址值。
*
* 右边:key.equals(k)
* 比较的是对象的内容是否相同。默认情况下比较的是地址值

    结论:    底层数据结构是哈希表。    哈希表依赖两个方法:hashCode()和equals()    执行流程:        首先判断哈希值是否相同,如果不同,就直接添加到集合。        如果相同,继续执行equals(),看其返回值,        如果是false,就直接添加到集合。        如果是true,说明元素重复不添加。

TreeSet: 在集中以升序对对象排序的集的实现。这意味着从一个TreeSet对象获得第一个迭代器将按升序提供对象。TreeSet类使用了一个TreeMap. (元素唯一且有序)
– -*元素顺序:使用元素的自然顺序对元素进行排序,或者根据创建 set时提供的 Comparator进行排序(比较器排序),具体取决于使用的构造方法。
— *底层算法:二叉树
—*元素要求, 加入自定义JavaBean

import java.util.TreeSet;/** * java.lang.ClassCastException:类型转换异常 *  * TreeSet集合有两种排序方式:至于哪两种排序方式,取决于他的构造器 *   * 自然排序:无参构造public TreeSet() *  * 比较器排序(Comparator): * * */public class TreeSetDemo {    public static void main(String[] args) {        //创建TreeSet集合,存储自定义对象        TreeSet<Student> ts = new TreeSet<Student>();        //给集合中添加Student对象        Student s = new Student("guodegang", 50);        Student s6 = new Student("liuyifei", 50);        Student s2 = new Student("zhangxueyou", 55);        Student s3 = new Student("amu", 45);        Student s4 = new Student("tf-boys", 18);        Student s5 = new Student("wangfeng", 49);        ts.add(s);        ts.add(s2);        ts.add(s3);        ts.add(s4);        ts.add(s5);        ts.add(s6);        //遍历集合        for (Student student : ts) {            System.out.println(student);        }    }}public class Student implements Comparable<Student>{    private String name;    private int age;    public Student(String name, int age) {        super();        this.name = name;        this.age = age;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    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 + "]";    }    //实现Comparable接口的同时必须实现这个比较法    @Override    public int compareTo(Student s) {        //就是写的是元素的比较规则,由你自己去动手写出        //按照学生的年龄进行排序        /**         * 两个对象进行比较:         * s         * this         */        int num = this.age - s.age;        //判断年龄是否相同,如果相同比较姓名        /**         * 写这个比较规则的时候注意两点:         * 1.他有主要条件,先按照主要条件进行排序         * 2.如果主要条件相同,就需要你自己分析出来他的次要条件,再去按照次要条件进行比较         */        int num2 = num==0?this.name.compareTo(s.name):num;        return num2;    }}
//规定排序规则的第二种方式import java.util.Comparator;import java.util.TreeSet;public class TreeSetDemo {    public static void main(String[] args) {        //创建集合,参数是一个接口,实际需要的是接口的实现类对象        //TreeSet<Student> ts = new TreeSet<Student>(new ComparatorImpl());        //使用匿名内部类来进行改进        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {            @Override            public int compare(Student s1, Student s2) {                int num = s1.getAge() - s2.getAge();                int num2 = num==0?s1.getName().compareTo(s2.getName()):num;                return num2;            }        });        //创建对象存入集合        Student s = new Student("guodegang", 50);        Student s6 = new Student("liuyifei", 50);        Student s2 = new Student("zhangxueyou", 55);        Student s3 = new Student("amu", 45);        Student s4 = new Student("tf-boys", 18);        Student s5 = new Student("wangfeng", 49);        ts.add(s);        ts.add(s2);        ts.add(s3);        ts.add(s4);        ts.add(s5);        ts.add(s6);        //遍历集合        for (Student student : ts) {            System.out.println(student);        }    }}public class Student{    private String name;    private int age;    public Student(String name, int age) {        super();        this.name = name;        this.age = age;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    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 + "]";    }}
  • 映射(Map): 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
    Map接口中的方法概述(创建集合测试方法):

    A:删除功能

            void clear():移除集合中的所有键值对元素            V remove(Object key):根据键移除键值对元素,并返回值
  • B:判断功能
            boolean containsKey(Object key):判断集合中是否包含指定的键            boolean containsValue(Object value):判断集合中是否包含指定的值            boolean isEmpty():判断集合是否为空
  • C:获取功能
Set<Map.Entry<K,V>> entrySet():获取键值对对象的集合,遍历键值对对象,            利用getKey(),getValue()取出键和值(理解即可) *                      V get(Object key):根据键获取值 *          Set<K> keySet():获取所有的键 *          Collection<V> values():获取所有的值 *      D:添加功能 *          V put(K key,V value):集合添加键值对 *      E:长度功能 *          int size():键值对对数。
//功能代码实例import java.util.Collection;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;public class MapDemo {    public static void main(String[] args) {        //创建一个Map集合        //键是学号--值是姓名        Map<Integer,String> map = new HashMap<Integer,String>();        //V put(K key,V value):集合添加键值对        map.put(1, "周杰伦");        map.put(2, "郭德纲");        map.put(3, "刘德华");        map.put(4, "张学友");        //void clear():移除集合中的所有键值对元素        //map.clear();        //V remove(Object key):根据键移除键值对元素,并返回值        //String remove = map.remove(1);        //System.out.println(remove);        //boolean containsKey(Object key):判断集合中是否包含指定的键        //boolean containsKey = map.containsKey(2);        //System.out.println(containsKey);        //boolean containsValue(Object value):判断集合中是否包含指定的值        //boolean containsValue = map.containsValue("周杰伦");        //System.out.println(containsValue);        //boolean isEmpty():判断集合是否为空        //System.out.println(map.isEmpty());        //int size():键值对对数。        //System.out.println(map.size());        //Collection<V> values():获取所有的值        /*        Collection<String> values = map.values();        Iterator<String> it = values.iterator();        while (it.hasNext()) {            System.out.println(it.next());        }*/        //Set<Map.Entry<K,V>> entrySet():获取键值对对象的集合,遍历键值对对象,        //利用getKey(),getValue()取出键和值(理解即可)        Set<Entry<Integer,String>> entrySet = map.entrySet();        for (Entry<Integer, String> entry : entrySet) {            System.out.println(entry.getKey()+"  "+entry.getValue());        }        System.out.println("--------------------");        //Set<K> keySet():获取所有的键        Set<Integer> keys = map.keySet();        for (Integer key : keys) {            //V get(Object key):根据键获取值            System.out.println(key+"  "+map.get(key));        }    }}

HashTable: 实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。

HashMap: 实现一个映象,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个)。

WeakHashMap: 实现这样一个映象:通常如果一个键对一个对象而言不再被引用,键/对象对将被舍弃。这与HashMap形成对照,映象中的键维持键/对象对的生命周期,尽管使用映象的程序不再有对键的引用,并且因此不能检索对象。

TreeMap: 实现这样一个映象,对象是按键升序排列的。

Set和List都是由公共接口Collection扩展而来,所以它们都可以使用一个类型为Collection的变量来引用。这就意味着任何列表或集构成的集合都可以用这种方式引用,只有映射类除外(但也不是完全排除在外,因为可以从映射获得一个列表。)所以说,把一个列表或集传递给方法的标准途径是使用Collection类型的参数。

**

- 面试题:

ArrayList与LinkedList的相同点与不同点
相同点:有顺序的,元素可以重复
(1)ArrayList特点:
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
(2)LinkedList特点:
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
Vector 还是ArrayList,哪一个更好,为什么?
要回答这个问题不能一概而论,有时候使用Vector比较好;有时是ArrayList,有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案,因为这要看你用它们干什么。下面有4个要考虑的因素:

(1)API

(2)同步处理

(3)数据增长性

(4)使用模式

下面针对这4个方面进行一一探讨

API
在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述,Vector类似于ArrayList.。所有从API的角度来看这两个类非常相似。但他们之间也还是有一些主要的区别的。

同步性

Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销。

数据增长

从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。

使用模式

在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?

这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选择其他的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的使用缺比较慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,所有你要明白它也会带来额外的开销。

最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。

0 1
原创粉丝点击