JAVA 集合整体架构

本文将从架构层面分析java的集合设计。

先看一张简化的整体架构:

还有一大块Map架构:

List架构解析

list接口架构图:

List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。

List接口继承于Collection接口,它可以定义一个 允许重复的有序集合 。因为List中的元素是有序的,所以我们可以通过使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。

List所代表的是 有序的Collection ,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有: ArrayList、LinkedList、Vector、Stack。

实例化:

List <data-type> list1= new ArrayList();  
List <data-type> list2 = new LinkedList();  
List <data-type> list3 = new Vector();  
List <data-type> list4 = new Stack();  

ArrayList

它使用动态数组来存储不同数据类型的重复元素。 ArrayList类维护插入顺序,并且是非线程安全的。 存储在ArrayList类中的元素可以被随机访问。实例:

public class Main {
	public static void main(String args[]) {
		ArrayList<String> list = new ArrayList<String>();
		list.add("Ravi");
		list.add("Vijay");
		list.add("Ravi");
		list.add("Ajay");
		Iterator itr = list.iterator();
		while (itr.hasNext()) {
			System.out.println(itr.next());
		}
	}
}
///////结果
Ravi
Vijay
Ravi
Ajay

LinkedList

内部使用一个双链表来存储元素。 它可以存储重复的元素。 它维护插入顺序。 在LinkedList中,操作非常快,因为不需要移位。

public class Main {
	public static void main(String args[]) {
		LinkedList<String> al = new LinkedList<String>();
		al.add("111");
		al.add("222");
                //移除头部
		al.removeFirst();
                //添加到尾部
		al.addLast("333");
		Iterator<String> itr = al.iterator();
		while (itr.hasNext()) {
			System.out.println(itr.next());
		}
	}
}
///////结果
222
333

Vector

Vector使用动态数组来存储数据元素。 它类似于ArrayList。 但是,它是同步的,并且包含许多不属于Collection框架的方法。

Stack

栈是Vector的子类。 它实现了后进先出的数据结构,即堆栈。

public class Main {
	public static void main(String args[]) {
		Stack<String> stack = new Stack<String>();
		stack.push("aaa");
		stack.push("bbb");
		stack.pop();
		Iterator<String> itr = stack.iterator();
		while (itr.hasNext()) {
			System.out.println(itr.next());
		}
	}
}
、、、
aaa


ArrayList、LinkedList、Vector、Stack 源码分析请见

罗政:Java集合:(一) ArrayList源码解析(JDK8)

罗政:Java集合:(二) LinkedList源码解析(JDK8)

罗政:Java集合:(三) 线程安全 List 分析(JDK8)

Set架构解析

set架构图:

https://pic4.zhimg.com/v2-1292c02fc5c1b8f76e56ac9117837b27_b.jpg

无序的元素集,不允许存储重复的元素。 在Set中最多可以存储一个空值。 Set由HashSet、LinkedHashSet和TreeSet实现。

构造:

Set s1 = new HashSet<>();  
Set s2 = new LinkedHashSet<>();  
Set s3 = new TreeSet<>();  

(1)HashSet

HashSet 是一个没有重复元素的集合。它是由 HashMap 实现的,不保证元素的顺序 (这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致),而且 HashSet 允许使用 null 元素。HashSet 是非同步的,如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须保持外部同步。 HashSet 按 Hash 算法来存储集合的元素,因此具有很好的存取和查找性能。

HashSet 的实现方式大致如下,通过一个 HashMap 存储元素,元素是存放在 HashMap 的 Key 中,而 Value 统一使用一个 Object 对象。

HashSet 使用和理解中容易出现的误区:

  • HashSet 中存放 null 值。**HashSet 中是允许存入 null 值的,但是在 HashSet 中仅仅能够存入一个 null 值。
  • HashSet 中存储元素的位置是固定的。HashSet 中存储的元素的是无序的,这个没什么好说的,但是由于 HashSet 底层是基于 Hash 算法实现的,使用了 hashcode,所以 HashSet 中相应的元素的位置是固定的。

(2)LinkedHashSet

LinkedHashSet 继承自 HashSet,其底层是 基于 LinkedHashMap 来实现的 ,有序,非同步。LinkedHashSet 集合同样是根据元素的 hashCode 值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候, LinkedHashSet 将会以元素的添加顺序访问集合的元素。

(3)TreeSet

TreeSet 是一个有序集合,其底层是基于 TreeMap 实现的,非线程安全。TreeSet 可以确保集合元素处于排序状态。

源码分析请见:

罗政:Java集合:(五) HashSet源码解析JDK8)


Queue架构解析

队列接口维护先进先出的顺序。 它可以定义为一个有序列表,用于保存将要处理的元素。 有许多子类,如PriorityQueue、Deque和ArrayDeque,它们实现了Queue接口。

实例化:

Queue<String> q1 = new PriorityQueue();  
Queue<String> q2 = new ArrayDeque();  

PriorityQueue

PriorityQueue类实现Queue接口。 它保存按优先级处理的元素或对象。 PriorityQueue不允许空值存储在队列中。

用法实例:

public class Main {
	public static void main(String args[]) {
		PriorityQueue<String> queue = new PriorityQueue<String>();
		queue.add("aaaa");
		queue.add("bbbbb");
		queue.add("ccccc");
		queue.add("ddddd");
		//返回头部元素,队列为空 则抛出异常
		System.out.println("head:" + queue.element());
		// 返回头部元素,队列为空返回null
		System.out.println("head:" + queue.peek());
		//返回头部元素,且删除头部元素 , 队列为空 则抛出异常
		queue.remove();
		// 返回头部元素,且删除头部元素 , 队列为空返回null
		queue.poll();
		System.out.println("remove,poll 后:");
		Iterator<String> itr2 = queue.iterator();
		while (itr2.hasNext()) {
			System.out.println(itr2.next());
		}
	}
}  
///////////////结果
head:aaaa
head:aaaa
remove,poll 
ccccc
ddddd

Deque

Deque接口扩展了Queue接口。 在Deque中,我们可以从两边删除和添加元素。 Deque代表一个双端队列,它使我们能够在两端执行操作。

ArrayDeque为Deque的实现子类。ArrayDeque比ArrayList和Stack快,而且没有容量限制。 实例:

public class Main {
	public static void main(String[] args) {
//Creating Deque and adding elements  
		Deque<String> deque = new ArrayDeque<String>();
		deque.add("aaaa");
		deque.add("bbb");
		deque.add("ccc");
		//添加到头
		deque.addFirst("----");
		// 添加到尾部
		deque.addLast("dddd");
		for (String str : deque) {
			System.out.println(str);
		}
	}
}
///////////////结果
----
aaaa
bbb
ccc
dddd

Map架构解析

map架构图:

Map 与 List、Set 接口不同,它是由一系列键值对组成的集合,提供了 key 到 Value 的映射。如果您必须根据键来搜索、更新或删除元素,那么Map非常有用。

子类有:HashMap ,LinkedHashMap ,TreeMap等。

Map不允许重复键,但可以有重复值。 HashMap和LinkedHashMap允许空键和值,但TreeMap不允许任何空键和值。

HashMap

存储键值对,有下面几个特点:

  • 允许key/value为null,但是key只能有一个null。
  • 非线程安全,多个线程同时操作同一个HashMap实例所做的修改在线程间不同步。
  • 遍历时不保证任何顺序,跟元素插入顺序或者访问顺序无关。
  • 进行遍历时如果执行HashMap的remove(Object key)或者put(Object value)方法时会快速失败,抛出异常ConcurrentModificationException。遍历时删除元素只能通过Iterator本身的remove()方法实现。

LinkedHashMap

HashMap的无序的,及内部的存储顺序和输出顺序不一定相同(除非输入key的hash值是有序的)。而LinkedHashMap则是有序的。LinkedHashMap同样支持null键和值,并且值可以重复,它也是不同步的。

TreeMap

TreeMap类是一个基于红黑树的实现。 它提供了一种按顺序存储键值对的有效方法。

源码解析请见:

罗政:Java集合:(三) HashMap 原理(JDK8)

罗政:Java集合:(四) HashMap 红黑树(JDK8)


强烈推荐一篇 干货,精华 博客:

Java架构师修炼

支付宝打赏 微信打赏

如果文章对您有帮助,您可以鼓励一下作者