目录
数组
int a = new int[10];
int a = {1,2,3,4,5};
a.length
Arrays.toString(a)
Arrays.copyOf(a,a.length)
Arrays.binarySearch(a,4)
Collection接口
List接口
-
ArrayList:基于数组实现的动态数组,支持快速随机访问和动态扩容。在尾部插入和删除元素的时间复杂度为O(1),在中间插入和删除元素的时间复杂度为O(n)。
-
LinkedList:基于双向链表实现的链表,支持快速的插入和删除操作,但随机访问的性能较差。插入和删除元素的时间复杂度为O(1),但随机访问的时间复杂度为O(n)。
-
Vector:与ArrayList类似,也是基于数组实现的动态数组,但是Vector是线程安全的,所有的方法都是同步的。由于同步化的操作,性能通常比ArrayList略低。
-
Stack:栈的实现类,继承自Vector类,提供了后进先出(LIFO)的操作。通常用于实现栈数据结构,可以通过push()方法压栈,pop()方法出栈等。
这些List实现类在不同的场景下有不同的应用,你可以根据具体的需求选择合适的实现类。ArrayList通常在需要快速随机访问的场景下使用,LinkedList适用于频繁的插入和删除操作,Vector和Stack通常用于需要线程安全的情况下。
ArrayList
- 基于动态数组实现,内部使用数组来存储元素。
- 支持随机访问和快速的元素检索,因为可以通过索引直接访问元素。
- 在尾部添加和删除元素的性能较好,但在中间或开头插入或删除元素时,需要移动元素,性能较差。
- 不是线程安全的,不适合在多线程环境中使用。
ArrayList<Integer> list = new ArrayList<>();
list.add(a);
list.add(a,index);
list.remove(index);
list.remove(a);
list.get(index);
list.set(index,a);
list.clear();
list.contains(Objec o);
Vector
- 类似于ArrayList,也是基于动态数组实现的,但是Vector是线程安全的。
- 所有方法都被同步(synchronized)了,可以在多线程环境中安全地使用。
- 在性能上通常比ArrayList差,因为所有方法都有同步开销。
- 由于同步的特性,通常不推荐在单线程环境中使用。
LinkedList
- 基于双向链表实现,内部使用链表来存储元素。
- 支持快速的插入和删除操作,因为只需要修改指针即可,不需要移动元素。
- 不支持随机访问,只能通过迭代器或从头部或尾部开始遍历列表。
- 不是线程安全的,不适合在多线程环境中使用。
Stack
当需要使用栈时,Java已不推荐使用Stack,而是推荐使用更高效的ArrayDeque;
Stack<Integer> s = new Stack<>();
s.push(num);
s.pop();
s.empty();
s.peek(); //获取栈顶元素
s.search(num); //判端元素num是否在栈中,如果在返回1,不在返回-1。
Queue接口
Java中的Queue是一个接口,Deque是Queue的子类。
Deque接口
LindkedList和ArrayDeque实现了Deque接口。当需要使用队列时也首选ArrayDeque
Set接口
HashSet
- HashSet 是基于 HashMap 实现的,它使用 HashMap 来存储元素。
- 它不保证元素的顺序,允许存储null元素。
- HashSet 中只存储键,而值都被设置为一个固定的值,通常是一个常量对象(比如
PRESENT = new Object()
)。 - HashSet 中的元素都是唯一的,当我们尝试向 HashSet 中添加重复元素时,HashSet 会通过 HashMap 的键的唯一性来保证元素不重复。
LinkedHashSet
- LinkedHashSet继承自HashSet,在HashSet的基础上增加了维护插入顺序的功能。
- LinkedHashSet允许存储null元素,元素是唯一的。
- LinkedHashSet内部使用LinkedHashMap来存储元素,LinkedHashMap通过双向链表维护了插入顺序。
TreeSet
- TreeSet是基于红黑树实现的,它可以保持元素的自然排序或者自定义排序。
- TreeSet不允许存储null元素,元素是唯一的。
- 通过比较器(Comparator)或元素的自然顺序来对元素进行排序。
Map接口
HashMap
- 特点:
- HashMap是基于哈希表实现的,采用了数组和链表(或红黑树)的组合结构。
- 允许存储null键和null值。
- 不保证元素的顺序,不是线程安全的。
- 底层架构:
- HashMap内部维护了一个Entry数组,每个Entry包含键值对。
- 使用哈希函数计算键的哈希值,然后将其映射到数组的索引位置。
- 哈希冲突:当多个键经过哈希函数计算后映射到同一个数组索引位置时,就会发生哈希冲突。HashMap 使用链表或红黑树来解决冲突,如果链表长度过长,则链表会转换为红黑树以提高性能。
- 扩容: 当 HashMap 中的元素数量超过了数组容量的75%,HashMap 会进行扩容操作。扩容后,数组的大小会变为原来的两倍,并重新计算元素的索引位置。
- 应用场景:
- 单线程环境下的普通使用场景。
- 数据量不大,对性能要求较高的场景。
Map<int ,String> map = new HashMap<int ,String>();
map.get(key);
map.set(key,value);
map.containsKey(key);
map.containsValue(value);
map.remove(Object key)
map.isEmpty();
map.size();
遍历
使用entrySet遍历
//迭代器
Iterator iter = map1.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
String val = (String)entry.getValue();
}
//foreach
for(Map.Entry<String, String> map:map1.entrySet()) {
map.getKey();
map.getValue();
}
使用keySet遍历
//迭代器
Set<String> keys = map.keySet();
Iterator<String> it = keys.iterator();
while(it.hasNext()){
String key = it.next();
String value = map.get(key);
}
//foreach
Set<String> keys = map.keySet();
for (String key : keys) {
String value = map.get(key);
}
LinkedHashMap
- 特点:
- LinkedHashMap继承自HashMap,在HashMap的基础上增加了维护插入顺序或访问顺序的功能。
- 通过双向链表维护了插入顺序或访问顺序。
- 允许存储null键和null值。
- 底层架构:
- LinkedHashMap内部同样维护了一个Entry数组,但每个Entry还包含了指向前后节点的指针,以维护顺序。
- 应用场景:
- 需要保持元素的插入顺序或访问顺序的场景,如LRU缓存算法实现。
ConcurrentHashMap
- 特点:
- ConcurrentHashMap是HashMap的线程安全版本,使用了锁分段技术来提高并发性能。
- 不允许存储null键和null值。
- 支持高并发的读取和写入操作。
- Java 1.7: Java 1.7 中的 ConcurrentHashMap 使用了分段锁(Segment)的机制来实现并发控制,每个Segment是一个独立的哈希表,拥有自己的锁,不同的段之间可以并发进行操作。
- 在读多写少的情况下表现更好
- ConcurrentHashMap 中的读操作不需要加锁,只有在写操作(插入、删除等)时才需要加锁。
- ConcurrentHashMap 支持并发的扩容操作,可以在不影响其他线程的情况下进行扩容。在读多写少的情况下,扩容操作不会频繁进行,因此对性能的影响相对较小。
- Java 1.8: Java 1.8 中的 ConcurrentHashMap 放弃了分段锁的机制,采用了 CAS (Compare and Swap)操作和 synchronized 来实现并发控制。
- 它使用了一种称为 CAS+链表的算法,用于解决哈希冲突。
- ConcurrentHashMap 使用了数组+链表+红黑树的结构来存储键值对。当链表长度超过阈值时,会将链表转换为红黑树,以提高查找、插入和删除操作的性能。
- 应用场景:高并发的多线程环境下,如服务器端的缓存系统或并发容器,特别是
HashTable
HashMap和Hashtable都是Java中用于存储键值对的数据结构,它们之间有以下区别:
- 特点:
- Hashtable是早期的哈希表实现,与HashMap类似,但是是线程安全的。
- 不允许存储null键和null值。
- 不保证元素的顺序。
- 底层架构:
- Hashtable内部同样使用了数组和链表的组合结构来存储键值对。
- 对整个数据结构加锁,使得每次操作都是原子的,从而实现线程安全性。
- Hashtable所有的方法都使用 synchronized 关键字进行同步,因此在多线程环境下会存在较高的竞争和性能瓶颈。
- 应用场景:
- 多线程环境下需要保证线程安全的场景,如早期的Java应用程序。
-
迭代器:
- HashMap的迭代器(Iterator)是fail-fast的,即在迭代过程中如果其他线程对HashMap进行了结构性修改(增加或删除元素),会抛出ConcurrentModificationException异常。
- Hashtable的迭代器是fail-safe的,它在迭代过程中不会抛出异常,但是不能保证迭代器的快照与其遍历过程完全一致。
-
扩容机制:
- HashMap在扩容时是将原数组中的元素重新分配到新数组中,采用2倍扩容的机制。
- Hashtable在扩容时是将原数组中的元素重新分配到新数组中,采用2倍+1的扩容机制。但是,HashTable 在扩容时需要锁定整个数据结构,因此在扩容过程中其他线程无法进行操作,可能会导致性能下降。
注: HashMap和Hashtable的扩容机制不同是因为它们在设计和实现上的不同。HashMap使用负载因子来决定扩容阈值,默认负载因子是0.75。当HashMap的大小超过负载因子与当前容量的乘积时,就会进行扩容。而Hashtable则使用固定的2倍加1的扩容机制。