【黑马程序员】Java集合类

来源:互联网 发布:周末网络国债理财 编辑:程序博客网 时间:2024/06/07 00:19

 -------android培训、java培训、期待与您交流! ----------

1. Java集合        

        Java集合类也成Java容器类,是Java中实用的工具,实现了一些常用的数据结构和方法,方便开发者调用。集合类和数组不一样,数组中可以存放基本类型的变量也可以存放对象,容器类中只能存放对象(实际上是对象的引用)。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类,如下图是Collection接口的继承树:


        如图所示:Set和List接口是Collection的两个子接口,它们分别代表了无序集合和有序集合;Queue是Java提供的队列实现,有点类似于List。

        下图显示的是Map接口的子接口及其及继承树:


        所有的Map实现类用于保存具有映射关系的数据,上图显示了Map的众多实现类,它们的功能各有不同,但都是存放的键值对。

2.Set集合

    Set集合类似于一个罐子,放进去的元素是无序的。Set集合类似于Collection但Set集合不能存放相同的元素。

    Set判断两个元素是否相同不是通过 == 而是 equals() 方法。

1) HashSet类

    Hash是Set的典型实现类,也是Set中使用最多的类。HashSet使用Hash算法存放元素,有很好的存取和查找性能。

    特点:

    ①不能保证元素的排列顺序。

    ②不同步。当两个线程同时访问一个HashSet时,假设他们同时修改HashSet元素,必须通过代码保证同步性。

    ③集合元素可以是null。

    判断元素相同标准:

    HashSet通过equals()以及hashCode()两个方法判断两个元素是否相同。如果两个元素euqals()比较返回true但hashCode不同,HashCode同样认为这是两个不同的元素。

     示例:

class TestA {    public boolean equals(Object obj){        return true;    }}    //类B的hashCode()方法总是返回1,但没有重写其equals()方法class TestB {    public int hashCode(){        return 1;    }}    //类C的hashCode()方法总是返回2,且有重写其equals()方法class TestC {    public int hashCode(){        return 2;    }    public boolean equals(Object obj) {        return true;    }}public class HashSetTest {    public static void main(String[] args) {        HashSet hs = new HashSet();        //分别向hs集合中添加两个TestA对象,两个TestB对象,两个TestC对象        hs.add(new TestA());        <span style="font-family: Arial, Helvetica, sans-serif;">hs</span><span style="font-family: Arial, Helvetica, sans-serif;">.add(new TestA());      //返回true,添加成功。两个对象的equals()虽然为true,但hashCode不同,HashSet视为不同对象</span>        <span style="font-family: Arial, Helvetica, sans-serif;">hs</span>.add(new TestB());        hs.add(new TestB());  <span style="font-family: Arial, Helvetica, sans-serif;">//返回true,添加成功。对象的hashCode恒定返回2,所以相等,但两个对象指向不同。使用Object类的equals()方法进行比较时返回false, ,HashSet视为不同对象</span>        hs.add(new TestC());        hs.add(new TestC()); //添加失败,两个对象的equals()返回true,且hashCode返回2相等,所以无法添加元素,返回false        System.out.println(hs);    }}

    HashSet中euqlas()返回ture并且hashCode()相等才判断为同一元素,如果euqals()为true但hashCode()不同,则存放元素时按hash值存放。如果hashCode相等,但equals为false,则会在原来hashcode位置使用链式存放,会导致性能下降。

2) LinkedHashSet

    使用链表维护容器内元素的顺序,输出时与添加元素的顺序一致。其他特性与HashSet一致。

    示例:

public class LinkedHashSetTest {    public static void main(String[] args) {        LinkedHashSet lhs = new LinkedHashSet();        lhs.add("天空飘来5个字");        lhs.add("那都不是事");        System.out.println(lhs);        //删除 天空飘来5个字        lhs.remove("天空飘来5个字");        //重新添加 天空飘来5个字        lhs.add("天空飘来5个字");        System.out.println(lhs);    }}

3)TreeSet

         TreeSet是SortedSet接口的实现类。TreeSet中的元素总是有序的。

        TreeSet采用红黑树存放元素。当一个元素被添加到TreeSet中时,系统会调用对象的compareTo(Object obj)方法与容器的其他元素比较确定元素的存放位置。所以添加到TreeSet的元素必须实现Comparable接口,否则在添加时会抛出ClassCastException异常。如以下代码所示:

class Tmp {}public class TreeSetExceptionTest {public static void main(String[] args) {TreeSet ts = new TreeSet();//向TreeSet集合中添加两个Tmp对象ts.add(new Tmp());  //可以正常添加,因为容器没有元素,不用进行比较//由于Tmp类未实现Comparable接口所以,当向TreeSet中插入第2个元素时,系统会抛出异常ts.add(new Tmp());  }}

    对于TreeSet而言,判断两个元素是否相等唯一的条件是compareTo()方法返回0;

    总结:HashSet和TreeSet是Set的两个典型实现。一般情况下HashSet的性能好于TreeSet,因为TreeSet需要维护红黑树来保持集合元素的次序。HashSet的子类LinkedHashSet的性能没有HashSet高,同样是要维护一个链表。但应为有链表所以LinkedHashSet的遍历速度优于HashSet,EnumSet的性能最好。

3. List集合

    List集合代表一个有序的,可重复的集合,集合的中每个元素都有对应的索引,可通过该索引随机访问集合中的元素。对应的数据结构为线性表。

    1) List接口
    List接口是Collection接口的子接口,扩展了一些线性表特有的方法:
    示例:

public class ListTest {    public static void main(String[] args) {        List names = new ArrayList();        //向names集合中添加三个元素        names.add(new String(""));        names.add(new String("张三"));        names.add(new String("李四"));        System.out.println(names);        //将新字符串对象插入在第二个位置        names.add(1 , new String("王五"));        for (int i = 0 ; i < names.size() ; i++ ) {            System.out.println(names.get(i));        }        //删除第三个元素        names.remove(2);        System.out.println(names);        //判断指定元素在List集合中位置:输出1,表明位于第二位        System.out.println(names.indexOf(new String("王五")));         //将第二个元素替换成新的字符串对象        names.set(1, new String("王五"));        System.out.println(names);        //将names集合的第二个元素(包括)        //到第三个元素(不包括)截取成子集合        System.out.println(names.subList(1 , 2));    }}

    List判断元素相等是通过equals()方法。List接口中的indexOf()、remove()等需要差早和比较元素的方法都是通过equals()方法确定元素是否相等,而不是通过判断元素的引用对象是否是同一对象。
    示例:

class A {    public boolean equals(Object obj) {        return true;    }}public class ListTest2 {    public static void main(String[] args) {        List names = new ArrayList();        names.add(new String("张三"));        names.add(new String("李四"));        names.add(new String("王五"));        System.out.println(names);        //删除集合中A对象,将导致第一个元素被删除        names.remove(new A());             System.out.println(names);        //删除集合中A对象,再次删除集合中第一个元素        names.remove(new A());             System.out.println(names);    }}

        2) ArrayList和Vertor实现类
       ArrayList和Vertor是List的两个典型实现类。两者内部使用数组存放元素,所以其内部有一个Object[]的动态数组。
       ArrayList和Vertor使用initialCapacity设置数组的长度,当元素数量超出数组的长度是,数组容量会自动增加。用户在使用两个集合类是无需关心initialCapacity的大小,系统会自动以及增长。当需要一次性添加大量元素时,可以将initialCapacity属性设置为一个较大的数字已减少系统扩充数组容量的次数,提升性能。
    示例:

 public static void main(String[] args) {    ArrayList<String> list = new ArrayList<>(20);  //设置初始化容量为20,如不设置默认容量为10    list.ensureCapacity(500);  //使用ensureCapacity()方法重行设置数组容量    list.trimToSize(); //将ArrayList长度缩减到当前数组当前元素的数量,可以使ArrayList占用的空间最小化 }

Vertor与ArrayList不同的是前者是线程安全的,当性能较ArrayList低,一般情况下不推荐使用。

4. Queue集合

        Queue用于模拟队列这种数据结构,队列是指FIFO的容器。队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入在队列的尾部,访问元素操作会返回队列头部的元素。
        Queue定义了如下几个方法:
  • vod add(Object e)将指定元素插入队尾
  • Object elenent() 获取队列头部的元素,但不删除该元素
  • boolean offer(Object e)将指定元素加入此队列的尾部,性能较好
  • Object peek()获取队列头部的元素,但不删除该元素。
  • Object poll()获取队列头部的元素,并删除该元素。
        1)PriorityQueue实现类
        这是一个标准额队列实现类,PriorityQueue保存队列元素的顺序并不是按照加入的顺序,而是按队列元素的大小进行排序。当调用peek()和poll()方法是,是去除队列中最小的元素。下面是该类的用法:
public class PriorityQueueTest {    public static void main(String[] args) {        PriorityQueue pq = new PriorityQueue();        //下面代码依次向pq中加入四个元素        pq.offer(6);        pq.offer(-3);        pq.offer(9);        pq.offer(0);        //输出pq队列,并不是按元素的加入顺序排列,        //而是按元素的大小顺序排列,输出[-3, 0, 9, 6]        System.out.println(pq);        //访问队列第一个元素,其实就是队列中最小的元素:-3        System.out.println(pq.poll());    }}
        注意:PriorityQueue不允许插入null对象

        2)Deque和ArrayDeque实现类
        Deque是Queue的子接口,它代表一个双端队列,Deque不仅可以当作队列使用还可以当作栈使用。Deque提供了一个典型的实现类:ArrayDeque,从该名称来看,这是一个基于数组实现的双端队列。下面示例将ArrayDeque当作堆栈来使用:
public class ArrayDequeTest {    public static void main(String[] args)  {        ArrayDeque stack = new ArrayDeque();        //依次将三个元素push入"栈"        stack.push("大学英语");        stack.push("高等数学");        stack.push("大学物理");        //输出:[大学英语, 高等数学 , 大学物理]        System.out.println(stack);        //访问第一个元素,但并不将其pop出"栈",输出:大学物理        System.out.println(stack.peek());        //依然输出:[大学英语, 高等数学 , 大学物理]        System.out.println(stack);        //pop出第一个元素,输出:大学物理        System.out.println(stack.pop());        //输出:[大学英语, 高等数学]        System.out.println(stack);    }}

5. HashMap和Hashtable集合

        它们都是Map接口的实现类,它们之间的区别如下:
Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点;但如果有多了线程访问同一个Map对象时,使用Hashtable实现类更好。
Hashtable不允许使用Null作为key和value,但HashMap可以使用null作为key或者value。
        下面演示null作为HashMap的key和value的情况:
public class NullInHashMap {    public static void main(String[] args) {        HashMap hm = new HashMap();        //试图将两个key为null的key-value对放入HashMap中        hm.put(null , null);        hm.put(null , null);    //①        //将一个value为null的key-value对放入HashMap中        hm.put("a" , null);    //②        //输出Map对象        System.out.println(hm);    }}
        HashMap判断两个key相等的标准是:两个key通过equals()比较返回true,两个key的hashCode值相等。
        HashMap判断两个value相等的标准是:两个value通过equals()表叫返回true。下面程序演示了hashtable判断两个key相等和两个value相等的标准。
class A {    int count;    public A(int count) {        this.count = count;    }    //根据count的值来判断两个对象是否相等。    public boolean equals(Object obj) {        if (obj == this)            return true;        if (obj!=null &&            obj.getClass()==A.class) {            A a = (A)obj;            return this.count == a.count;        }        return false;    }    //根据count来计算hashCode值。    public int hashCode() {        return this.count;    }}class B {    //重写equals()方法,B对象与任何对象通过equals()方法比较都相等    public boolean equals(Object obj) {        return true;    }}public class HashtableTest {    public static void main(String[] args)  {        Hashtable ht = new Hashtable();        ht.put(new A(60000) , "大学英语");        ht.put(new A(87563) , "高等数学");        ht.put(new A(1232) , new B());        System.out.println(ht);        //只要两个对象通过equals比较返回true,        //Hashtable就认为它们是相等的value。        //由于Hashtable中有一个B对象,        //它与任何对象通过equals比较都相等,所以下面输出true。        System.out.println(ht.containsValue("测试字符串"));  //①        //只要两个A对象的count相等,它们通过equals比较返回true,且hashCode相等        //Hashtable即认为它们是相同的key,所以下面输出true。        System.out.println(ht.containsKey(new A(87563)));   //②        //下面语句可以删除最后一个key-value对        ht.remove(new A(1232));    //③        //通过返回Hashtable的所有key组成的Set集合,        //从而遍历Hashtable每个key-value对        for (Object key : ht.keySet()) {            System.out.print(key + "---->");            System.out.print(ht.get(key) + "\n");        }    }}


0 0
原创粉丝点击