Java集合类介绍

一、集合与数组

      java数组的长度是固定的,在同一个数组中只能存放相同类型的数据。数据可以存放基本类型数据,也可以存放引用类型的数据。

      在创建java数组时,必须明确指定数组的长度,数组一旦创建,其长度不能被改变。为了使程序员能方便地存储和操纵数目不固定的一组数据,JDK类库提供了java集合,所有java集合类都位于java.util包中。与java数组不同,java集合中不能存放基本数据类型,而只能存放对象的引用。


二、集合主要类型

      Set、List、Map统称为java集合。

      Set:集合中的对象不按特定方式排序,并且没有重复对象。它的有些实现类能对集合中的对象按特定方式排序;

      List:集合中的对象按照索引位置排序,可以有重复对象,允许按对象在集合中的索引位置检索对象,List与数组有些相似;

      Map:集合中的每一个元素包含一对键对象和值对象,集合中没有重复的键对象,值对象可以重复。它的有些实现类能对集合中的键对象进行排序。


三、java主要集合类的类框图

      如图所示:实线边框是实现类,虚线边框是抽象类,点线边框是接口;


Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是Set和ListSet中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式

Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value

Iterator,所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
1.hasNext()是否还有下一个元素。
2.next()返回下一个元素。

3.remove()删除当前元素。


四、Set、List、Map

1、SetSet里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。

Set接口主要有两个实现类:HashSet和TreeSet。HashSet类按照哈希算法来存取集合中的对象,存取速度较快。HashSet类还有一个子类LinkedHashSet类,它不仅实现了哈希算法,而且实现了链表数据结构,链表数据结构能提高插入的删除元素的性能。TreeSet类实现了SortedSet接口,具有排序功能。

HashSet 类和 TreeSet 类

“集合框架”支持 Set 接口两种普通的实现:HashSet 和TreeSet。在更多情况下,您会使用 HashSet 存储重复自由的集合。考虑到效率,添加到 HashSet 的对象需要采用恰当分配散列码的方式来实现hashCode() 方法。虽然大多数系统类覆盖了 Object 中缺省的hashCode()实现,但创建您自己的要添加到 HashSet 的类时,别忘了覆盖 hashCode()。当您要从集合中以有序的方式抽取元素时,TreeSet 实现会有用处。为了能顺利进行,添加到TreeSet 的元素必须是可排序的。 “集合框架”添加对 Comparable 元素的支持,在排序的“可比较的接口”部分中会详细介绍。我们暂且假定一棵树知道如何保持java.lang 包装程序器类元素的有序状态。一般说来,先把元素添加到 HashSet,再把集合转换为TreeSet 来进行有序遍历会更快。

为优化 HashSet 空间的使用,您可以调优初始容量和负载因子。TreeSet 不包含调优选项,因为树总是平衡的,保证了插入、删除、查询的性能为log(n)

HashSet 和 TreeSet 都实现 Cloneable 接口。

2、List:主要特征是其元素以线性方式存储,集合中允许存放重复对象。

List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

与 set 不同,列表通常允许重复的元素。更正式地说,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。

List 接口在 iteratoraddremoveequals 和 hashCode 方法的协定上加了一些其他约定,超过了 Collection 接口中指定的约定。为方便起见,这里也包括了其他继承方法的声明。

List 接口提供了 4 种对列表元素进行定位(索引)访问方法。列表(像 Java 数组一样)是基于 0 的。注意,这些操作可能在和某些实现(例如 LinkedList 类)的索引值成比例的时间内执行。因此,如果调用方不知道实现,那么在列表元素上迭代通常优于用索引遍历列表。

List 接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator 接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。

List 接口提供了两种搜索指定对象的方法。从性能的观点来看,应该小心使用这些方法。在很多实现中,它们将执行高开销的线性搜索。

List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法。

List接口主要的实现类包括:  ArrayList和LinkedList。

ArrayList--ArrayList代表长度可变的数组。允许对元素进行快速的随机访问,但是向ArrayList中插入与删除元素的速度较慢。

LinkedList--在实现中采用链表数据结构。对顺序访问进行了优化,向List中插入和删除元素的速度较快,随机访问速度则相对较慢。随机访问是指检索位于特定索引位置的元素。LinkedList单独具有addFirst()、addLast()、getFirst()、getLast()、removeFirst()和removeList()方法,这些方法使得LinkedList可以作为堆栈、队列和双向队列来使用。

Collection是集合接口
    |————Set子接口:无序,不允许重复。
    |————List子接口:有序,可以有重复元素。

    区别:Collections是集合类

    Set和List对比:
    Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
    List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

    Set和List具体子类:
    Set
     |————HashSet:以哈希表的形式存放元素,插入删除速度很快。

    List
     |————ArrayList:动态数组
     |————LinkedList:链表、队列、堆栈。

3、Map: 接口不是 Collection 接口的继承。而是从自己的用于维护键-值关联的接口层次结构入手。按定义,该接口描述了从不重复的键到值的映射。它是一种把键对象和值对象进行映射的集合,它的每一个元素都包含一对键对象和值对象,而值对象仍可以是Map类型,以此类推,这样就形成了多级映射。通过Map的put(Object key,Object value)方法向集合中加入元素,通过Map的get(Object key)方法来检索与键对象对应的值对象。

实现类:HashMap、Hashtable、LinkedHashMap和TreeMap。

        HashMap 类和 TreeMap 类

“集合框架”提供两种常规的 Map 实现:HashMap 和TreeMap。和所有的具体实现一样,使用哪种实现取决于您的特定需要。在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按顺序遍历键,那么TreeMap 会更好。根据集合大小,先把元素添加到 HashMap,再把这种映射转换成一个用于有序键遍历的 TreeMap 可能更快。使用HashMap 要求添加的键类明确定义了 hashCode() 实现。有了TreeMap 实现,添加到映射的元素一定是可排序的。我们将在排序中详细介绍。

为了优化 HashMap 空间的使用,您可以调优初始容量和负载因子。这个TreeMap 没有调优选项,因为该树总处于平衡状态。

HashMap 和 TreeMap 都实现Cloneable 接口。

Hashtable 类和 Properties 类是Map 接口的历史实现。我们将在Dictionary 类、Hashtable 类和 Properties 类中讨论。

映射的使用示例

以下程序演示了具体 Map 类的使用。该程序对自命令行传递的词进行频率计数。HashMap 起初用于数据存储。后来,映射被转换为TreeMap 以显示有序的键列列表。

import java.util.*;
public class MapExample {
  public static void main(String args[]) {
    Map map = new HashMap();
    Integer ONE = new Integer(1);
    for (int i=0, n=args.length; i<n; i++) {
      String key = args[i];
      Integer frequency = (Integer)map.get(key);
      if (frequency == null) {
        frequency = ONE;
      } else {
        int value = frequency.intValue();
        frequency = new Integer(value + 1);
      }
      map.put(key, frequency);
    }
    System.out.println(map);
    Map sortedMap = new TreeMap(map);
    System.out.println(sortedMap);
  }
}

HashMap 

HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的

Hashtable

Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

ConcurrentHashMap

线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

LinkedHashMap

LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。

TreeMap

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;


五、主要实现类的区别

Vector和ArrayList
1,vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。
2,如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。
3,如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据时其它元素不移动。
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。

arraylist和linkedlist
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

HashMap与TreeMap
1、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
2、在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。
两个map中的元素一样,但顺序不一样,导致hashCode()不一样。
同样做测试:
在HashMap中,同样的值的map,顺序不同,equals时,false;
而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。

HashTable与HashMap
1、同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的。
2、HashMap允许存在一个为null的key,多个为null的value 。
3、hashtable的key和value都不允许为null。


注:Java集合框架是最常被问到的Java面试问题,要理解Java技术强大特性就有必要掌握集合框架。这里有一些实用问题,常在核心Java面试中问到。

  1、什么是Java集合API

  Java集合框架API是用来表示和操作集合的统一框架,它包含接口、实现类、以及帮助程序员完成一些编程的算法。简言之,API在上层完成以下几件事:

  ● 编程更加省力,提高城程序速度和代码质量

  ● 非关联的API提高互操作性

  ● 节省学习使用新API成本

  ● 节省设计新API的时间

  ● 鼓励、促进软件重用

  具体来说,有6个集合接口,最基本的是Collection接口,由三个接口Set、List、SortedSet继承,另外两个接口是Map、SortedMap,这两个接口不继承Collection,表示映射而不是真正的集合。

  2、什么是Iterator

  一些集合类提供了内容遍历的功能,通过java.util.Iterator接口。这些接口允许遍历对象的集合。依次操作每个元素对象。当使用 Iterators时,在获得Iterator的时候包含一个集合快照。通常在遍历一个Iterator的时候不建议修改集合本省。

  3、Iterator与ListIterator有什么区别?

  Iterator:只能正向遍历集合,适用于获取移除元素。ListIerator:继承Iterator,可以双向列表的遍历,同样支持元素的修改。

  4、什么是HaspMap和Map?

  Map是接口,Java 集合框架中一部分,用于存储键值对,HashMap是用哈希算法实现Map的类。

  5、HashMap与HashTable有什么区别?对比Hashtable VS HashMap

  两者都是用key-value方式获取数据。Hashtable是原始集合类之一(也称作遗留类)。HashMap作为新集合框架的一部分在Java2的1.2版本中加入。它们之间有一下区别:

  ● HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允许null值作为key和value,而Hashtable不可以)。

  ● HashMap没法保证映射的顺序一直不变,但是作为HashMap的子类LinkedHashMap,如果想要预知的顺序迭代(默认按照插入顺序),你可以很轻易的置换为HashMap,如果使用Hashtable就没那么容易了。

  ● HashMap不是同步的,而Hashtable是同步的。

  ● 迭代HashMap采用快速失败机制,而Hashtable不是,所以这是设计的考虑点。

  6、在Hashtable上下文中同步是什么意思?

  同步意味着在一个时间点只能有一个线程可以修改哈希表,任何线程在执行hashtable的更新操作前需要获取对象锁,其他线程等待锁的释放。

  7、什么叫做快速失败特性

  从高级别层次来说快速失败是一个系统或软件对于其故障做出的响应。一个快速失败系统设计用来即时报告可能会导致失败的任何故障情况,它通常用来停止正常的操作而不是尝试继续做可能有缺陷的工作。当有问题发生时,快速失败系统即时可见地发错错误告警。在Java中,快速失败与iterators有关。如果一个iterator在集合对象上创建了,其它线程欲“结构化”的修改该集合对象,并发修改异常 (ConcurrentModificationException) 抛出。

  8、怎样使Hashmap同步?

  HashMap可以通过Map m = Collections.synchronizedMap(hashMap)来达到同步的效果。

  9、什么时候使用Hashtable,什么时候使用HashMap

  基本的不同点是Hashtable同步HashMap不是的,所以无论什么时候有多个线程访问相同实例的可能时,就应该使用Hashtable,反之使用HashMap。非线程安全的数据结构能带来更好的性能。

  如果在将来有一种可能—你需要按顺序获得键值对的方案时,HashMap是一个很好的选择,因为有HashMap的一个子类 LinkedHashMap。所以如果你想可预测的按顺序迭代(默认按插入的顺序),你可以很方便用LinkedHashMap替换HashMap。反观要是使用的Hashtable就没那么简单了。同时如果有多个线程访问HashMap,Collections.synchronizedMap()可以代替,总的来说HashMap更灵活。

  10、为什么Vector类认为是废弃的或者是非官方地不推荐使用?或者说为什么我们应该一直使用ArrayList而不是Vector

  你应该使用ArrayList而不是Vector是因为默认情况下你是非同步访问的,Vector同步了每个方法,你几乎从不要那样做,通常有想要同步的是整个操作序列。同步单个的操作也不安全(如果你迭代一个Vector,你还是要加锁,以避免其它线程在同一时刻改变集合).而且效率更慢。当然同样有锁的开销即使你不需要,这是个很糟糕的方法在默认情况下同步访问。你可以一直使用Collections.sychronizedList来装饰一个集合。

  事实上Vector结合了“可变数组”的集合和同步每个操作的实现。这是另外一个设计上的缺陷。Vector还有些遗留的方法在枚举和元素获取的方法,这些方法不同于List接口,如果这些方法在代码中程序员更趋向于想用它。尽管枚举速度更快,但是他们不能检查如果集合在迭代的时候修改了,这样将导致问题。尽管以上诸多原因,oracle也从没宣称过要废弃Vector。


参考:http://www.cnblogs.com/leeplogs/p/5891861.html

          https://blog.csdn.net/u014136713/article/details/52089156










猜你喜欢

转载自blog.csdn.net/qq_34611579/article/details/79867350