Java数据结构

目录

Collection接口

List接口

ArrayList

Vector

LinkedList

Stack

Queue接口

Deque接口

Set接口

HashSet

LinkedHashSet

TreeSet

Map接口

HashMap

LinkedHashMap

ConcurrentHashMap

HashTable


数组

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接口

  1. ArrayList:基于数组实现的动态数组,支持快速随机访问和动态扩容。在尾部插入和删除元素的时间复杂度为O(1),在中间插入和删除元素的时间复杂度为O(n)。

  2. LinkedList:基于双向链表实现的链表,支持快速的插入和删除操作,但随机访问的性能较差。插入和删除元素的时间复杂度为O(1),但随机访问的时间复杂度为O(n)。

  3. Vector:与ArrayList类似,也是基于数组实现的动态数组,但是Vector是线程安全的,所有的方法都是同步的。由于同步化的操作,性能通常比ArrayList略低。

  4. 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的扩容机制。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

惊雲浅谈天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值