Java集合 Set(集)、List(列表)、Map(映射)

提到集合之前,先说说数组Array和集合的区别:

  1. 数组是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型)
  2. JAVA集合可以存储和操作数目不固定的一组数据。
  3. 若程序时不知道究竟需要多少对象,需要在空间不足时自动扩增容量,则需要使用容器类库,array不适用。  
    注意:使用相应的toArray()和Arrays.asList()方法可以相互转换。

在这里插入图片描述

Collection接口

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个 Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。

Set

Set接口继承Collection接口,它表示数学意义上的集合概念。Set中不允许集合中存在重复项,通过对象的equals()方法来检查同等属性,set中的元素是无序的。由于Set接口提供的数据结构是数学意义上集合概念的抽象,因此它需要支持对象的添加、删除,而不需提供随机访问。故Set接口与Collection的接口相同。
  HashSet:使用HashMap的一个集的实现。虽然集定义成无序,但必须存在某种方法能相当高效地找到一个对象。使用一个HashMap对象实现集的存储和检索操作是在固定时间内实现的.
  TreeSet: 在集中以升序对对象排序的集的实现。这意味着从一个TreeSet对象获得第一个迭代器将按升序提供对象。TreeSet类使用了一个TreeMap.
  HashSet和TreeSet都实现Cloneable接口。为了能顺利进行,添加到TreeSet的元素必须是可排序的。

List

List接口继承了Collection 接口以定义一个允许重复项有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。
实际上有两种List: 一种是基本的ArrayList,其优点在于随机访问元素,另一种是更强大的LinkedList,它并不是为快速随机访问设计的,而是具有一套更通用的方法。
LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。内部由双向链表实现的
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
  List list = Collections.synchronizedList(new LinkedList(…));
  
ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
  
Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Map

Map接口不是Collection接口的继承。而是从自己的用于维护键-值关联的接口层次结构入手。按定义,该接口描述了从不重复的键到值的映射。

  • 改变操作允许您从映射中添加和除去键-值对。键和值都可以为null。但是,您不能把Map作为一个键或值添加给自身。
  • 查询操作允许您检查映射内容
  • 提供可选视图方法允许您把键或值的组作为集合来处理。

HashMap 类和 TreeMap 类
“集合框架”提供两种常规的Map实现:HashMap和TreeMap。和所有的具体实现一样,使用哪种实现取决于您的特定需要。在Map中插入、删除和定位元素,HashMap是最好的选择。但如果您要按顺序遍历键,那么TreeMap会更好。根据集合大小,先把元素添加到HashMap,再把这种映射转换成一个用于有序键遍历的TreeMap可能更快。使用HashMap要求添加的键类明确定义了hashCode()实现。有了TreeMap实现,添加到映射的元素一定是可排序的。
  为了优化HashMap空间的使用,您可以调优初始容量和负载因子。这个TreeMap没有调优选项,因为该树总处于平衡状态。
  
HashMap和TreeMap都实现Cloneable接口。
HashTable: 实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。
HashMap: 实现一个映象,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个)。
WeakHashMap: 实现这样一个映象:通常如果一个键对一个对象而言不再被引用,键/对象对将被舍弃。这与HashMap形成对照,映象中的键维持键/对象对的生命周期,尽管使用映象的程序不再有对键的引用,并且因此不能检索对象。
TreeMap: 实现这样一个映象,对象是按键升序排列的。

ConCurrentHashMap

JDK 1.7 ConcurrentHashMap实现采用分段锁的机制,实现并发的更新操作,底层采用数组+链表的存储结构。ConcurrentHashMap最核心的两个核心静态内部类包括:Segment和HashEntry。
JDK 1.8 ConcurrentHashMap的实现已经抛弃了Segment分段锁机制,而是采用CAS + Synchronized来保证并发更新的安全,底层采用数组+链表+红黑树的存储结构。而HashMap在1.8版本中也对存储结构进行了优化,采用数组+链表+红黑树的方式进行数据存储,红黑树可以有效的平衡二叉树,带来插入、查找性能上的提升。

HashMap与HashTable有什么区别?

  • 继承的父类:Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类,但二者都实现了Map接口。
  • contains方法:HashMap把contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
  • 是否允许null:HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允许null值作为key和value,而Hashtable不可以)。
  • 同步性:HashMap不是同步的,而Hashtable是同步的。
  • 遍历方式:Hashtable、HashMap都使用了 Iterator。但由于历史原因,Hashtable还使用了Enumeration的方式 。
  • 初始化和扩容方式:HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。具体扩容时,Hashtable将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

什么是HaspMap和Map?

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

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

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

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

基本的不同点是Hashtable同步HashMap不是的,所以无论什么时候有多个线程访问相同实例的可能时,就应该使用Hashtable,反之使用HashMap。非线程安全的数据结构能带来更好的性能。
  如果在将来有一种可能—你需要按顺序获得键值对的方案时,HashMap是一个很好的选择,因为有HashMap的一个子类 LinkedHashMap。所以如果你想可预测的按顺序迭代(默认按插入的顺序),你可以很方便用LinkedHashMap替换HashMap。反观要是使用的Hashtable就没那么简单了。同时如果有多个线程访问HashMap,Collections.synchronizedMap()可以代替,总的来说HashMap更灵活。

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

你应该使用ArrayList而不是Vector是因为默认情况下你是非同步访问的,Vector同步了每个方法,你几乎从不要那样做,通常有想要同步的是整个操作序列。同步单个的操作也不安全(如果你迭代一个Vector,你还是要加锁,以避免其它线程在同一时刻改变集合).而且效率更慢。当然同样有锁的开销即使你不需要,这是个很糟糕的方法在默认情况下同步访问。你可以一直使用Collections.sychronizedList来装饰一个集合。
  事实上Vector结合了“可变数组”的集合和同步每个操作的实现。这是另外一个设计上的缺陷。Vector还有些遗留的方法在枚举和元素获取的方法,这些方法不同于List接口,如果这些方法在代码中程序员更趋向于想用它。尽管枚举速度更快,但是他们不能检查如果集合在迭代的时候修改了,这样将导致问题。尽管以上诸多原因,Oracle也从没宣称过要废弃Vector。

总结

如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

集合中哪些类是线程安全的

vector
stack
hashtable
enumeration
ConcurrentHashmap

猜你喜欢

转载自blog.csdn.net/qq_32458133/article/details/89206295