Java中容器的使用


 

由上面的图可以非常清楚的看到,Java中的容器的继承结构

在顶层有很多接口,这些接口声明了很多的基本的抽象方法,之后的许多类按照不同的方式实现这些接口,

同时可能在增加一些自己的方法,从而形成了不同功能的容器,比如:ArrayList类与LinkedList类都继承了List接口,

但是他们在实现List的接口时,方法体并不一样,这样一来就形成了不一样的容器,这些容器都位于java.util包中

 

接下来我们将一一介绍这些容器的功能,当然我们不可能将容器的所有的知识全部不漏的讲解,我们只是将一些

思想讲解一下,使我们对容器有一个整体的认识,至于一些具体的方法如何使用,需要在用时查询官方文档

首先我们先对List、Set、Queue、Map接口的功能做一个介绍:

①List类型的容器实际上就相当于一个长度可变的

“对象数组”,和向量的概念是相同的

②Set类型的容器实际上相当于一个“对象集合”

  这样的容器中是不允许加入重复的元素的,并且

  对于其中的元素并没有一种排序的概念,和集合

  是一个概念

③Queue类型的容器实际上相当于一个“对象队列”

④Map类型的容器中的每个元素包含一对儿键对象和

   值对象,即在关键字对象和值对象之间产生一种映射关系,通过键对象类查找值对象

  接下来,我们介绍逐一介绍这些具体的容器都有什么功能

 

  实例:

     Collection<String> c = new ArrayList<String>

(Array.asList("A","B","C"));

一、Collection 接口

这个接口只一个抽象度非常高的接口,这其中声明了那些实现了他的容器类都具有的特性方法,也就是诸如:

ArrayList类、LinkedList类、HashSet类等其中的继承自Collection接口的方法的实现方式都是一样的,

比如:boolean add(E e) 这个方法在所有的这些类中都是将一个元素加入容器当中;

即Collection中的抽象方法都是从“一堆元素”的概念上抽象出来的Collection接口的常用抽象方法有:

① public Boolean add(E e) 

   向容器当中添加一个元素

②boolean addAll(Collection<? extends E> c)

   向当前的容器中加入指定的Collection类型的容器的所有元素

③void clear()

   移除容器当中的所有的元素

④boolean contains(Object obj)

  判定当前的容器中是否有指定的元素,我们想肯定调用了equals()方法了

⑤boolean containsAll(Collection<?> c)

  判定当前容器中是否包含了,指定的容器中的所有的元素

⑥boolean isEmpty()

     判定当前的容器是否为空

   ⑦Iterator<E> iterator()

     返回当前容器对应的迭代器

   ⑧boolean remove(Object obj)

     如果容器当中含有该元素,则将之删除

   ⑨boolean retainAll(Collection<?> C)

     将当前的容器中的元素和指定的容器元素取交集存入当前容器中

   10.int size ()

      返回容器的元素数目

   11.Object[] toArray()

      根据当前容器中的元素创建一个对象数组

二、实用类Collections

在上面的图中的右下角有两个实用类:Collections类

和Arrays类,Arrays类中的静态方法主要是操作数组的,而Collection类中也有很多静态的方法是用来操作List、Set、Queue、Map类型的容器的,

这两个实用类都位于java.util包中

在Arrays类中也有一个操纵容器的方法

static    List<T>

asList(T... a)

返回有可变参数列表创建的List类型的容器对象,并返回容器的引用,如:

String [] strArray = {“123”,”wr”,”fsdf”};

List <String> ls = Array.asList(strArray);

在Collection类中的静态方法,能够实现容器中元素的替换、删除、排序、整个复制等等,具体的就不在多讲

三、List(列表)

  像数组一样,List也能建立数字索引与对象的关联,表达的是数据结构中的线性表的概念,List接口的常用实现类是:ArrayList和LinkedList

 

  ①ArrayList类

    就像名子显示的那样,ArrayList是线性表中的数组,也就是说这个类中的数据域实际上就是一个对象数组,

因此,他有数组的一切的特性,还有同时封装在一起的方法

因此对于查找来说,速度是非常的快的,但是删除与插入操作就相对较慢,而且ArrayList是线程不安全的

 

  ②LinkedList类

     就像名字显示的那样,LinkedList是线性表当中的链表,也就是说这个类中的数据域实际上就是一个对象链表,

还有同时封装在一起的方法,值的注意的是内部的这个链表是一个双向的循环链表,

也就是说,插入语删除操作是非常快的,但是查找较慢

     另外需要非常注意的是,LinkedList类还继承了Queue接口,

LinkedList类中单独还有addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等方法,

这就是的LinkedList类的对象能够用作队列和栈来使用,同样,LinkedList是线程不安全的

四、Set(集合)

1、Set 中不接受任何重复的元素,如果试图将重复的对象加到同一个Set对象当中的话,是不能加入的;所谓的相同的对象,

是根据equals(参数)方法来判定的,这就要涉及到该方法的重写问题了,这在之前已经讨论过了

 

2、一般Set最常用于判定一个指定元素的归属性问题,即判定一个元素是否已经在在当前的集合当中了!可见查找

是Set型容器的最常用的、最重要的方法了;Set与Collection具有完全一样的接口,实际上Set就是Collection,只是应用的行为不同罢了!

3、虽然Set有HashSet、TreeSet、LinkedSet三种实现类,但是在《thinking in java》 中明确指出,

对于查找而言HashSet类失效率最高的一个类,所以在没有特殊的要求时,使用HashSet是最好的选择

 

<HashSet类>

1、这个类的内部有一个HasMap实例对象作为其成员域,

这说明在HashSet类的实例对象中的元素都是按照哈希表的形式存储在HashMap对象中了,这也就说明了,HashSet类的查找效率是非常高的

2、通过add(参数)向HashSet对象当中,添加元素时,首先会调用hasCode()方法,计算出该元素的存放位置,

比较该位置的元素是否与要添加的元素相同,所以又要调用equals()方法;同时在查找时也要调用hashCode方法;

这就要求我们将equals()方法和hashCode方法同时重写,保证两个方法相匹配,即保证:

当两个元素对象“相等时”,那么两个元素对象应该生成相同的散列码

这就用到了另一份文档“Java中的hashCode()方法的深入剖析.doc”(其中那个例子非常有代表性)

我们在获得这个容器中的每个元素时,可以使用简化的for语句实现,这是允许的,可以查阅一下简化的for语句的使用前提

 

五、Queue(队列)

也就是说,Queue类型的容器专门用来用作容器来使用的

具有队列的特性

①利用LinkedList类型的容器实现普通的队列、双向队列、栈等数据结构(LinkedList类同时实现了Queue接口),

我们可以通过将LinkedList的对象向上转型到Queue,这样一来的话就只能使用继承自Queue接口的专门用于队列的方法,

使LinkedList对象看起来像一个完完全全的队列容器;对于双向队列,java中有一个专门用于实现双端队列的接口Deque,位于java.uitl包中,

而且LinkedList类也实现了该接口,所以同样可以将LinkedList对象向上转型形成一个单纯的双端队列

②PriorityQueue(优先级队列)

所谓的优先级队列就是在该队列的内部有一个堆的数据结构,实现了每次出对的元素,都是优先级最高的元素;

但是程序是如何知道每个元素的优先级的呢,这里有两种方式:自然方式和人为指定方式

所谓的自然方式是指,如果容器中的元素对象是String、Integer、或者是Character类型的内嵌数据类型的话,

那么这种优先级是程序已经自行规定好的了,即“较小”的拥有较高的优先级;但是对于实际问题中的很多的元素都不是内嵌的数据类型,

那么就要人为的指定优先级的设定规则,解决这一问题的方法就是,使元素类实现Comparable

接口,这个接口中只有一个方法

public int compareTo(Object obj)

只要在元素类中将该方法实现的话,那么程序就会自行的根据这个方法来设定优先级,如:

 

注意:对于PriorityQueue来说,他的遍历器不能保证按照优先级次序便利所有的元素

 

六、Map(映射)

Map(映射)是一种将键对象和值对象进行映射的集合,需要注意的是如果值对象是Map类型的话那么,就形成了多级映射;

在实现该接口的类中效率最高的一个就是HashMap类,一般在没有特殊的要求的情况下最好及使用这个类型的容器,

这个容器的用法和前面讲过的Hashtable容器非常相近,可以参照该部分的内容进行理解,

其中最终要的一点是对于hashCode()和euqals()方法的重写;

另外需要注意的一点是:如果在哈希表的应用中,迭代性能比查找性能更加站上风,那么在设定初始容量时,

就应该尽量的不让初始容量太高,而同时让装填因子设定的得尽量的高;如果是说实际使用哈希表的时候,

有非常多的键值对要加入到该容器中的话,使用足够大的初始容量创建该容器,将使得映射关系更加有效的存贮,

因为如果初始容量设定的过于小的话,那么容器就会按照规则不断的进行扩充,那么就会耗费非常多的时间

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM