关于Java中集合:List,Set ,Map,整理

来源:互联网 发布:如何备案域名 编辑:程序博客网 时间:2024/05/21 06:25

集合List,Set,Map

List

List是存取有序集合的接口,并且允许相同的元素,实现Collection接口。

  • ArrayList:线程不安全的,及不同步,是基于动态数组实现的,(对象数组Object[]为基础,进行容器操作),对于随机的访问查询,比较快,因为带有索引。对于增删效率会低于LinkedList,因为ArrayList删除,增加要移动数据。

  • LinkedList:同样也是线程不安全的,是基于链表的数据结构实现的。LinkedList.Node<E>,对于随机访问查询比较慢,因为会移动指针,直到找到元素为止,但对于增删效率比较高。

  • Vector:线程安全的,实现与ArrayList类似,基于动态数组,由于线程安全,效率会低于ArrayList。

  • stack: 继承Vector。拥有Vector的属性,并添加了pop,push等方法,

Set

set相比较List都是现实Collection接口 ,但是Set是无序的,并且不允许存重复的元素,允许有一个null值
具体的实现结构

- –SortedSet接口 Set接口 –HashSet实现类 - –LinkedHashSet实现类
  • HashSet ,允许存null值,基于HashMap的存取HashSet的实现是不同步的。如果多个线程同时访问一个集合,而其中至少一个线程修改了该集合,那么它必须 保持外部同步。

    HashSet类:// Dummy value to associate with an Object in the backing Mapprivate static final Object PRESENT = new Object();//构造public HashSet() {      map = new HashMap<>();}// add 方法public boolean add(E e) {      return map.put(e, PRESENT)==null;}// hashMap key也是不允许重复的,
  • TreeSet: 同样的,TreeSet是有 TreeMap 实例支持。但是不能存null元素。此类保证排序后的 set 按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序 进行排序,或按照在创建 set 时所提供的比较器进行排序。是一个有序集合,元素中安升序排序,缺省是按照自然顺序进行排序,意味着TreeSet中元素要实现Comparable接口;我们可以构造TreeSet对象时,传递实现了Comparator接口的比较器对象.

  • LinkedHashSet:集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,**当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。**LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。

HashSet是基于Hash算法实现的,其性能通常优于TreeSet,我们通常都应该使用HashSet,在我们需要排序的功能时,我门才使用TreeSet;

Map:

  • HashMap:适用于在Map中插入、删除和定位元素速度较快。线程不安全,允许有Null值。
  • Treemap:适用于按自然顺序或自定义顺序遍历键(key),线程不安全不允许有Null。
  • Hashtable:线程安全,API较早。
  • ConcurrentHashMap:分段锁,线程安全。

具体实现流程:

引用自Oracle技术官网:官网

几乎所有通用 Map 都使用哈希映射。这是一种将元素映射到数组的非常简单的机制,您应了解哈希映射的工作原理,以便充分利用 Map。

哈希映射结构由一个存储元素的内部数组组成。由于内部采用数组存储,因此必然存在一个用于确定任意键访问数组的索引机制。实际上,该机制需要提供一个小于数组大小的整数索引值。该机制称作哈希函数。在 Java 基于哈希的 Map 中,哈希函数将对象转换为一个适合内部数组的整数。您不必为寻找一个易于使用的哈希函数而大伤脑筋: 每个对象都包含一个返回整数值的 hashCode() 方法。要将该值映射到数组,只需将其转换为一个正值,然后在将该值除以数组大小后取余数即可。以下是一个简单的、适用于任何对象的 Java 哈希函数

int hashvalue = Maths.abs(key.hashCode()) % table.length;

HashMap存储流程


该图介绍了哈希映射的基本原理,但我们还没有对其进行详细介绍。我们的哈希函数将任意对象映射到一个数组位置,但如果两个不同的键映射到相同的位置,情况将会如何? 这是一种必然发生的情况。在哈希映射的术语中,这称作冲突。Map 处理这些冲突的方法是在索引位置处插入一个链接列表,并简单地将元素添加到此链接列表。因此,一个基于哈希的 Map 的基本 put() 方法可能如下所示

public Object put(Object key, Object value) {  //我们的内部数组是一个 Entry 对象数组  //Entry[] table;  //获取哈希码,并映射到一个索引  int hash = key.hashCode();  int index = (hash & 0x7FFFFFFF) % table.length;  //循环遍历位于 table[index] 处的链接列表,以查明  //我们是否拥有此键项 — 如果拥有,则覆盖它  for (Entry e = table[index] ; e != null ; e = e.next) {    //必须检查键是否相等,原因是不同的键对象    //可能拥有相同的哈希    if ((e.hash == hash) && e.key.equals(key)) {      //这是相同键,覆盖该值      //并从该方法返回 old 值      Object old = e.value;      e.value = value;      return old;    }  }  //仍然在此处,因此它是一个新键,只需添加一个新 Entry  //Entry 对象包含 key 对象、 value 对象、一个整型的 hash、  //和一个指向列表中的下一个 Entry 的 next Entry  //创建一个指向上一个列表开头的新 Entry,  //并将此新 Entry 插入表中  Entry e = new Entry(hash, key, value, table[index]);  table[index] = e;  return null;}
原创粉丝点击