首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java--集合类之Collection与Map

Java--集合类之Collection与Map

作者头像
SuperHeroes
发布2018-05-22 16:09:55
8660
发布2018-05-22 16:09:55
举报
文章被收录于专栏:云霄雨霁云霄雨霁

上一篇:Java--集合类之Vector、BitSet、Stack、Hashtable

  • 集合(Collection):一组单独的元素,通常应用了某种规则。在这里,一个 List(列表)必须按特定的顺序容纳元素,而一个Set(集)不可包含任何重复的元素。相反,“包”(Bag)的概念未在新的集合库中实现,因为“列表”已提供了类似的功能。
  • 映射(Map):一系列“键-值”对(这已在散列表身上得到了充分的体现)。从表面看,这似乎应该成为一个“键-值”对的“集合”,但假若试图按那种方式实现它,就会发现实现过程相当笨拙。这进一步证明了应该分离成单独的概念。另一方面,可以方便地查看 Map的某个部分。只需创建一个集合,然后用它表示那一部分即可。这样一来,Map 就可以返回自己键的一个Set、一个包含自己值的List 或者包含自己“键 -值”对的一个List。和数组相似,Map可方便扩充到多个“维”,毋需涉及任何新概念。只需简单地在一 个Map 里包含其他 Map。 

Collection和 Map可通过多种形式实现,具体由编程要求决定。可以得出,如果访问List集合中的元素,可以通过元素的索引访问;如果访问Map集合中的元素,可以通过元素的键来访问;如果访问Set集合中的元素,只能通过元素本身来访问。

Collection:

下面总结集合能做的所有事情(亦可对 Set和List 做同样的事情,尽管 List 还提供了一 些额外的功能)。Map不是从 Collection 继承的,所以要单独对待。 

  • boolean add(Object) *保证集合内包含了自变量。如果它没有添加自变量,就返回 false
  • boolean addAll(Collection) *添加自变量内的所有元素。如果没有添加元素返回 true
  • void clear() *删除集合内的所有元素
  • boolean contains(Object) 若集合包含自变量,就返回“true”
  • boolean containsAll(Collection) 若集合包含了自变量内的所有元素,就返回“true”
  • boolean isEmpty() 若集合内没有元素,就返回“true”
  • Iterator iterator() 返回一个反复器,以用它遍历集合的各元素
  • boolean remove(Object) *如自变量在集合里,就删除那个元素的一个实例。如果已进行了删除,就返回 “true”
  • boolean removeAll(Collection) *删除自变量里的所有元素。如果已进行了任何删除,就返回“true”
  • boolean retainAll(Collection) *只保留包含在一个自变量里的元素(一个理论的“交集”)。如果已进行了任何改变,就返回“真”
  • int size() 返回集合内的元素数量
  • Object[] toArray() 返回包含了集合内所有元素的一个数组

*这是一个“可选的”方法,有的集合可能并未实现它。若确实如此,该方法就会遇到一个 UnsupportedOperatiionException,即一个“操作不支持”

Lists:

List(接口) 顺序是 List 最重要的特性;它可保证元素按照规定的顺序排列。List 继承Collection 并添加了大量方法,以便我们在 List 中部插入和删除元素(只推荐对LinkedList 这样做)。List 也会生成一个 ListIterator(列表反复器),利用它可在一个列表里朝两个方向遍历,同时插入和删除位于列表中部的元素(同样地,只建议对 LinkedList这样做)

ArrayList:

由一个数组后推得到的,ArrayList内部封装了一个动态的、允许再分配的Object数组。用于替换原先的Vector。允许我们快速访问元素,但在从列表中部插入和删除元素时,速度却嫌稍慢。一般只应该用ListIterator对一 个ArrayList 进行向前和向后遍历,不要用它删除和插入元素;与 LinkedList相比,它的效率要低许多。

ArrayList是线程不安全的,Vector是线程安全的。但由于Vector是非常古老的集合类,性能很差,通常我们都不使用Vector,即使对线程安全有需求,ArrayList也可以通过一些手段实现线程安全。

LinkedList:

LinkedList类是List接口的实现类,这意味着它可以根据索引随机访问集合中的元素。同时,LinkedList内部是以链表的形式保存元素,所以它的删除、插入效率很高。同时,LinkedList还实现了Deque接口,可以被当成双端队列来使用,因此既可以用作“栈”,也可以用作“队列”。

Sets:

Set拥有与 Collection完全相同的接口,所以和两种不同的 List 不同,它没有什么额外的功能。相反,Set 完全就是一个Collection,只是具有不同的行为。在这里,一个Set只允许每个对象存在一个实例。 添加到 Set里 的对象必须定义equals(),从而建立对象的唯一性。一个 Set不能保证自己可按任何特定的顺序维持自己的元素。

HashSet:
  • 最常用的Set实现类,按Hash算法存储元素,具有很好的存取和查找性能。
  • 不能保证元素和排列顺序。
  • HashSet不是线程同步的。
  • 集合的元素值可以为Null.
  • HashSet判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()返回值相等。

创建一个新类并且要添加到一个HashSet中时,需要重写equals()方法和hashCode()方法,并且要保证两个对象equals()相等时hashCode()也要相等。

HashSet类有一个子类LinkedHashSet,子类在存储元素的时候会使用链表维护元素的次序,相对的,效率会较HashSet低一些。

LinkedHashSet:

HashSet的一个子类,也是根据hashCode()决定元素存储位置。但它同时用链表维护元素插入的顺序,这样使元素看起来像是以插入顺序保存的。也就是说当遍历LinkedHashSet时,LinkedHashSet会按元素添加顺序来遍历。

EnumSet:
  • EnumSet中所有key都必须是单个枚举类的枚举值,创建EnumSet时必须显式或隐式指定它的枚举类;
  • EnumSet内部以数组形式保存,所以这种形式非常紧凑、高效;
  • EnumSet根据枚举值在枚举类中的定义顺序排序;
  • EnumSet不允许加入null值。
TreeSet:

由一个“红黑树”后推得到的顺序 Set,是SortedSet接口的实现类。TreeSet可以保证元素的排序顺序。因为TreeSet是有序的,所以API提供了访问第一个、后一个、最后一个、前一个元素的方法,还提供了截取子TreeSet的方法。另外,TreeSet也不是线程同步的。

比较和排序问题:TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后按升序排列。所以我们放进TreeSet中的对象都必须保证其所属类实现了Comparable接口(该接口中声明了compareTo()方法)。Java类库中的很多类都实现有Comparable接口。

注意,实现compareTo()方法时,必须将比较对象强制转换为相同类型。可以这样说,如果想让TreeSet正常工作,集合中只能添加同种类型的对象。当然,要保证compareTo()方法和equals()方法返回的意义相同。

定制排序:如果想要改变排序方式,可以通过Comparator接口实现。该接口是一个函数式接口。在创建一个TreeSet对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。

Maps:

Map(接口) 维持“键-值”对应关系(对),以便通过一个键查找相应的值。 Map和Set有点类似,比如:

  • 如果把Map里所有key放一起看,就组成了一个Set(所有key没有顺序,不能重复),实际上Map有一个方法keySet()返回key组成的Set集合;
  • Map的key集和Set集合存储形式很像,Set有HashSet、LinkedHashSet、SortedSet、TreeSet、EnumSet等子接口和实现类,Map也有HashMap、LinkedHashMap、SortedMap、TreeMap、EnumMap等子接口和实现类。
HashMap:

基于散列表实现(用它代替Hashtable)。针对“键-值”对的插入和检索,这种形式具有最稳定的性能。

HashMap和Hashtable的关系类似于ArrayList和Vector的关系。Hashtable线程安全,性能比较差,现在很少使用;HashMap是线程不安全的,性能较好,可以实现线程安全,推荐使用。另外,HashMap允许使用null作为key或value,但Hashtable中key和value都不可以使用null.

为了成功地在HashMap和Hashtable中存储对象,用作key的对象必须实现equals()方法和hashCode()方法。

  • 判断两个key相等的标准是:两个key的equals()方法返回true,并且hashCode()返回值也相等。
  • 判断两个value相等的标准:只要两个对象通过equals()方法比较为true即可。

使用自定义类作为HashMap、Hashtable的key时,如果重写该类的equals()方法和hashCode()方法,必须保证两个方法判断标准一致, 即两个key通过equals()方法返回true时,它们的hashCode()返回值应该也相等。

TreeMap:

是SortedMap接口的一个实现类,在一个“红-黑”树的基础上实现。每个键值对即作为红黑树的一个结点。TreeMap保存结点时,需要对节点进行排序,所以我们会得到有顺序排列的键值对。

TreeMap的排列方式类似与TreeSet的排序方式:

  • 默认排序:TreeSet的所有key必须实现Comparable接口,而且所有key应该是同一类型的对象。
  • 定制排序:创建TreeMap对象时,传入一个Comparator对象,该对象负责对TreeMap中的key进行排序。采用定制排序时不要求Map的key实现Comparable接口。

类似于TreeSet中判断两个元素相等的标准,TreeMap判断两个key相等的标准是:两个key通过compareTo()方法返回0即可。

LinkedHashMap:

是HashMap的一个子类,使用双向链表维护key-value对的次序。该链表负责维护Map的迭代顺序,迭代顺序与插入的顺序保持一致。因为需要维护元素插入顺序,性能略低于HashMap。但因为使用链表,在迭代访问Map里的全部元素时将有较好的性能。

WeakHashMap:

与HashMap不同的是,HashMap的key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,其中所有的key所引用的对象都不会被垃圾回收器回收,HashMap也不会自动删除这些键值对;但WeakHashMap保留的是弱引用,如果WeakHashMap对象保存的key所引用的对象没有被其他强引用变量引用,这些key引用的变量可能被垃圾回收,WeakHashMap也可能自动删除这些键值对。

IdentityHashMap:

和HashMap的区别在于,它处理两个key相等比较独特:当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为它们相等。

EnumMap:
  • EnumMap中所有key都必须是单个枚举类的枚举值,创建EnumMap时必须显式或隐式指定它的枚举类;
  • EnumMap内部以数组形式保存,所以这种形式非常紧凑、高效;
  • EnumMap根据key的自然排序(即枚举值在枚举类中的定义顺序)来维护键值对顺序;
  • EnumMap不允许使用null作为key,但允许使用null作为value。
enum Season    //定义枚举类
{ SPRING, SUMMER, FALL, WINTER }

public class EnumMapTest{
    public static void main(String[] args){
        EnumMap enum = new EnumMap(Season.class);
        enum.put(Season.SUMMER,"夏日炎炎");
        enum.put(Season.SPRING,"春暖花开");
        System.out.println(enum);
    }
}

/********输出结果:********/
{SPRING=春暖花开, SUMMER=夏日炎炎}
各Map实现类性能分析:
  • EnumMap性能最好,但只能使用同一枚举类的枚举值作为key;
  • LinkHashMap要比HashMap慢一些,因为它需要维护一个链表;
  • IIdentityHashMap性能和HashMap差不多,因为两者有基本相似的实现;
  • TreeMap通常比HashMap和Hashtable要慢,因为它底层采用红黑树实现;
  • Hashtable通常比HashMap慢,因为它是一个古老的,线程安全的集合。

(各Map对应相应的Set类,性能分析也适用于上面的那些Set类)

包装线程不安全的集合

线程相关知识可以看这篇博客

上面讲到的ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是线程不安全的。如果程序中有多个线程需要访问以上这些集合,就可以使用Collections提供的类方法把这些集合包装成线程安全的集合。Collections提供了如下几个静态方法:

  • <T> Collection<T> synchronizedCollection(Collection<T> c):返回指定collection对应线程安全的collection。
  • static <T> List<T> synchronizedList(List<T> list):返回指定List对象对应线程安全的List对象。
  • static <K,T>Map<K,V> synchronizedMap(Map<K,V> m):返回指定Map对象对应线程安全的Map对象。
  • static <T>Set<T> synchronizedSet(Set<T> s):返回指定Set对象对应线程安全的Set对象。
  • static <K,T>SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m):返回指定SortedMap对象对应线程安全的SortedMap对象。
  • static <T>SortSet<T> synchronizedSortSet(SortSet<T> s):返回指定SortSet对象对应线程安全的SortSet对象。

例如需要在多线程中使用线程安全的HashMap对象:

HashMap m = Collection.synchronizedMap(new HashMap());
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Collection:
  • Lists:
    • ArrayList:
      • LinkedList:
      • Sets:
        • HashSet:
          • LinkedHashSet:
            • EnumSet:
              • TreeSet:
              • Maps:
                • HashMap:
                  • TreeMap:
                    • LinkedHashMap:
                      • WeakHashMap:
                        • IdentityHashMap:
                          • EnumMap:
                            • 各Map实现类性能分析:
                            • 包装线程不安全的集合
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档