您的位置:首页 > 编程语言 > Java开发

Java基础复习系列六之Java集合

2016-03-18 15:45 525 查看
Collection接口
  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。

如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:

    Iterator it = collection.iterator(); // 获得一个迭代子

    while(it.hasNext()) {

      Object obj = it.next(); // 得到下一个元素

    }

  由Collection接口派生的两个接口是List和Set。

 

Iterator接口也是Java集合框架的成员,但它与Collection系列,map系列的集合不一样,Collection系列集合,map系列集合主要用于盛装其他对象,而Iterator则主要用于遍历(即迭代访问)Collection集合中的元素,所以Iterator对象也被称为迭代器。

 

 

List接口
 List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。

和下面要提到的Set不同,List允许有相同的元素。

  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。

  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

 

ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。

LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率高。

Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。

Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。

 

如果涉及到“栈”、“队列”、“链表”等操作,应该考虑用List,具体的选择哪个List,根据下面的标准来取舍。

(01) 对于需要快速插入,删除元素,应该使用LinkedList。

(02) 对于需要快速随机访问元素,应该使用ArrayList。

(03) 对于“单线程环境” 或者 “多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类(如ArrayList)。对于“多线程环境,且List可能同时被多个线程操作”,此时,应该使用同步的类(如Vector)。

 

LinkedList:双向链表查找index位置的节点时,有一个加速动作:若index < 双向链表长度的1/2,则从前向后查找; 否则,从后向前查找。

 

 

ArrayList 插入元素会移动很多元素的位置,耗时,动态数组。

 

ArrayList是非线程安全;

而Vector是线程安全的,它的函数都是synchronized(同步)的,即都是支持同步的。

ArrayList适用于单线程,Vector适用于多线程。

 

ArrayList有3个构造函数,而Vector有4个构造函数。Vector除了包括和ArrayList类似的3个构造函数之外,另外的一个构造函数可以指定容量增加系数。

 

逐个添加元素时,若ArrayList容量不足时,“新的容量”=“(原始容量x3)/2 + 1”。

而Vector的容量增长与“增长系数有关”,若指定了“增长系数”,且“增长系数有效(即,大于0)”;那么,每次容量不足时,“新的容量”=“原始容量+增长系数”。若增长系数无效(即,小于/等于0),则“新的容量”=“原始容量 x 2”。

 

 

List 是一个接口,它继承于Collection的接口。它代表着有序的队列。

AbstractList 是一个抽象类,它继承于AbstractCollection。AbstractList实现List接口中除size()、get(int location)之外的函数。

AbstractSequentialList 是一个抽象类,它继承于AbstractList。AbstractSequentialList 实现了“链表中,根据index索引值操作链表的全部函数”。

ArrayList, LinkedList, Vector, Stack是List的4个实现类。

ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。

LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。

Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。

Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。

 

 

Set接口
Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。

  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。

  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Set接口不像List接口那样可以通过索引或去元素,只能通过Iterator()方法获取迭代器进行迭代。

Set中的add方法不同于Collection接口,有一个boolean类型的返回值,当集合中含有与某个元素equals相等的元素时返回false,无法添加,否则返回true。

 

HashSet类是Set接口的重要实现类,由哈希表(实际上是一个 HashMap 实例)支持。此类允许使用 null 元素

此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size

 

Set接口是通过equals方法来比较两元素是否相同的,但是如果有1000个元素,那么再添加一个元素,就要比较1000次,效率严重降低。所以HashSet类底层采用了HashMap来存储元素,而HashMap采用了哈希表的原理。

 

HashSet是通过两元素的HashCode()方法和equals()方法来判断两元素是否相等的。具体的判断顺序为:

  判断两个对象的hashCode是否相等。如果不相等,认为两个对象也不相等,完毕。如果相等转入2

  判断两个对象用equals方法运算是否相等。如果不相等,认为两个对象也不相等。如果相等,认为两个对象相等。

  之所以还要进行一次equals方法进行比较,是因为hashCode方法相等时,equals方法可能不相等。所以当我们使用HashSet添加元素时一定要重写该元素的HashCode方法和equals方法

假如我们放入一个自己定义的类实例的时候,比如Person类实例,这时候我们要自己重新hashcode和equal方法

 

如果只重写equals方法而不重写hashCode()方法,那么可以equals比较为true,但是hashCode返回不同,那么就会存入两个相同的元素

TreeSet : 红黑树算法。可以实现排序(创建 set 时提供的 Comparator 进行排序)等功能的集合,它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成按照“升序”排列。

在对大量信息进行检索的时候,TreeSet比AraayList更有效率,能保证在log(n)的时间内完成

TreeSet是实用树形结构来存储信息的,每个节点都会保存一下指针对象,分别指向父节点,左分支,右分支,相比较而言,ArrayList就是一个含有元素的简单数组了,正因为如此,它占的内存也要比ArrayList多一些。

 

 

TreeSet 集合只能添加同一种类型的对象。否则在添加对象的时候调用compareTo的时候,会抛出异常。

set有几个比较好的方法:

removeAll(Collection<?> c)

移除 set 中那些包含在指定 collection 中的元素(可选操作)。

boolean retainAll(Collection<?> c)

仅保留 set 中那些包含在指定 collection 中的元素(可选操作)。

containsAll(Collection<?> c)

如果此 set 包含指定 collection 的所有元素,则返回 true。

 

 

Map

Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。 Key和value都可以是任何引用类型的数据。Key不允许重复。则同一个map对象的任何两个key通过equals方法比较总是返回false。

 

如果把map里的所有key放在一起来看。他们就组成了一个set集合。实际上map确实包含了一个keySet的方法。

 

Java源码来看,Java是先实现了Map,然后通过包装一个所有value都为PRESENT 的静态对象的Map就实现了Set集合。 

 

Map map = new HashMap();

Map.put(“疯狂”,99);

Map.put(“java”,100);

 

System.out.print(Map.put(“java”,111)); // 输出100,覆盖的时候会返回原先的value

 

HashMap 和Hashtable 都是Map接口的典型实现类

两者区别:

Hashtable是一个线程安全的Map实现。但Hashmap是线程不安全的实现。所以HashMap比Hashtable的性能高一点。但如果有多个线程访问同一个Map对象,使用Hashtable实现类会更好。

Hashtable不允许使用null作为key和value,试图放进去会报出空指针异常,但是HashMap可以使用null作为key和value。

 

TreeMap 就是一个红黑树数据结构。每个key-value对应作为红黑树的一个节点。TreeMap存储key-value对节点时,需要根据key对节点进行排序。 TreeMap有两种排序形式

自然排序: TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则将会抛出classCastException异常。

定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中所有的key进行排序。

 

TreeMap 通常比hashMap要慢(尤其在插入删除,key-value对时更慢)。因为treemap底层采用红黑树来管理。

使用treemap的好处。treeMap中的key-value对总是处于有序状态。

对于一般的应用场景,多用hashMapo,因为hashMap正式为快速查询设计的(底层也是采用数组来存储key-value对)。

 

如何获取线程安全的集合:

可以采用如下方法:

 

Collection c = Collections.synchronizedCollection(new ArrayList());

List list = Collections.synchronizedCollection(new ArrayList());

Set s = Collections.synchronizedCollection(new HashSet());

Map m = Collections.synchronizedCollection(new HashMap());

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  set map 集合