集合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 Map private 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;
该图介绍了哈希映射的基本原理,但我们还没有对其进行详细介绍。我们的哈希函数将任意对象映射到一个数组位置,但如果两个不同的键映射到相同的位置,情况将会如何? 这是一种必然发生的情况。在哈希映射的术语中,这称作冲突。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;
}