面试集合-Collection-Map-List-Set-Queue

集合Collection

java中常用的容器有哪些?

常见容器有两种:Collection 和 Map 两种。
Collection 存储着对象的集合(单列),Map 存储着键值对(两个对象)的映射表(双列)。
Collection集合又分为List、Set、Queue,这三个子接口;
List集合中有ArrayList、LinkedList、Vector实现类;
Set集合中有TreeSet、HashSet、LinkedHashSet实现类;
Queue集合中有LinkedList、priorityQueue实现类;
Map集合中的实现类有:TreeMap、HashMap、LinkedHashMap、HashTable、ConcurrentHashMap;

Collection常用方法

方法名说明
boolean add(Object o)添加元素对象
boolean remove(Element e)删除元素对象
boolean isEmpty()判断集合是否为空
boolean contains(Object o)判断集合中是否存在指定元素
int size()返回集合对象
void clear()清空集合元素
Iterator< E > iterator()返回此集合元素的迭代器

2022/11/26 迭代器
Collection集合的对象可以使用迭代器遍历元素;

Collection→Set

说明
TreeSet基于红黑树实现,支持有序性操作。
TreeSet时间复杂度为O(logN)
HashSet基于哈希表实现,支持快速查找,不支持有序性操作。
HashSet时间复杂度为O(1)
使用Iterator遍历HashSet得到的结果是不准确的
LinkedHashSet具有HashSet的查找效率,且内部使用双向链表维护元素的插入顺序。

Set集合中,没有特殊方法,所有方法都继承于Collection集合

Collection→List

说明
ArrayList基于动态数组实现,支持随机访问
线程不安全
Vector基于动态数组实现,支持随机访问
线程安全
线程安全是有条件的:Vector的单个操作具有原子性=线程安全,如果两个原子操作复合而来,组合的方法是非线程安全的,需要使用锁来保证线程安全
LinkedList基于双向链表实现,只能顺序访问。
但可以快速地在链表中间插入和删除元素,
还可以用作栈、队列和双向队列

备注2022/8/20

  • 动态数组
    存储的是Object类型的变元素;数组长度是可以改变的的,如果长度不够,集合本身是会进行扩容的;

List常用方法

方法名说明
void add(int index,E e )向集合中添加元素
E remove(int index)删除指定索引值的元素
E set(int index,E e)修改执行索引值位置的元素
E get(int indxe)获取指定索引值处的元素
int size()获取集合中元素的个数

注意事项:

  • List的常用方法,所涉及的参数大多数有index
  • ArrayList常用方法:add、remove、set、get、size
  • LinkedList常用方法:addFirst、addLast、removeFirst、removeLast、getFirst、getLast。

Collection→Queue

说明
LinkedList用来实现双向队列
PriorityQueue基于堆结构实现,用它来实现优先队列

Map集合

说明
TreeMap基于红黑树实现
HashMap基于哈希表实现,线程不安全
HashTable基于哈希表实现,线程安全,
同一时刻多个线程可以同时写入HashTable并且不会导致数据不一致。
它是遗留类,不应该使用,替换的是 ConcurrentHashMap来支持线程安全,并且它的效率更高,因为它引入了分段锁
LinkedHashMap使用双向链表来维护元素的顺序(插入顺序 或者 最近最少使用顺序(LRU))

Map集合常用方法

方法名说明
V put(K,V)向集合中添加键值对元素,返回值
V remove( Object key)通过键删除集合中的元素,返回值
boolean containsKey(key)判断集合是否包含指定的键 key
boolean containsValue(value)判断集合是否包含指定的值 value
int size()返回集合的长度
void clear()清空集合
boolean isEmpty()判断是否为空
V get(Object key)根据键获取对应的值
Set< E> keySet()返回集合中所有键组成的Set集合
Collection< T> values()返回集合中所有值组成的Collection集合
Set<Map.entry<K,V>> entrySet()返回集合中所有键值对对象组成的Set集合

fail-fast和fail-safe有什么区别?

前者迭代期间不能修改元素、后者遍历的是复制后的集合
两者最大不同在于:fail-fast不允许在迭代期间修改集合元素,否则会报Concurrent Modification Exception;
fail-safe允许在迭代期间修改集合元素,它遍历的是集合元素的复制集合,缺点是对集合的修改,它不会察觉到;

fail-fast

  • 概念:
    快速失败;可能出现fail-fast机制的集合有ArrayList、HashMap。
  • 案例:
    在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
    迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
    这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出,因此,这个异常只建议用于检测并发修改的bug。
  • 场景:
    java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

fail-safe

  • 概念:
    安全失败;常见的fail-safe方式遍历的容器有ConcurrentHashMap 和 CopyOnWriteArrayList。
  • 案例:
    采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
    由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
  • 缺点:
    基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

Collection和Collections有什么区别?

前者是集合、后者是集合的工具类
Collection:是基本的集合接口,一个Collection代表一组Object(即Collection的元素),它的直接继承接口有List、Queue、Set接口。

Collections:不属于java的集合框架,是一个包装类,是集合类的一个工具类/帮助类。不能被实例化,服务于Collection框架。包含有关集合操作的静态、多态方法(对各种集合的搜索、排序、线程安全等操作)。
常用的方法有sort排序、reverse列表反转、shuffle随机排列指定的数组;

List、Map、Set三个接口,存取元素时,各有什么特点?

顺序可重复、无序不可重复、键值对无序
List:存储有顺序,且可以重复;
Set:存储无序的,不可以重复,具有唯一性;
Map:存储的是键值对,也是无序的;

List集合

ArrayList的扩容机制?

发生在add()方法被调用的时候,初始大小是10,每次扩容内存空间增加1.5倍

扩容实现方法:ensureCapacityInternal。
具体细节是:

  1. 通过calculateCapacity方法获取数据所需最小容量;
  2. 获取的最小容量通过ensureExplicitCapacity判断是否需要扩容,通过比较最小容量和元素存储空间的大小得出结论;需要则执行grow方法扩容。
  3. 具体扩容的实现采用的是oldCapacity>>1再加上oldCapacity实现;

add方法源码


// ArrayList的add方法 是java1.8版本的,1.9版本和这个不一样。
public boolean add(E e) {
       //扩容
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        return true;
    }
    
// ensureCapacityInternal就是用来扩容的,形参为最小扩容量
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    // 也就说 minCapacity(最小扩容量) = 实际数据所需要的内存空间
    // elementData是ArrayList集合中的属性,transient Object[] elementData; 
    // 在进行ArrayList对象创建的时候,它的构造方法中就对elementData进行了赋值。
}

// calculateCapacity(Object[] elementData, int minCapacity)  计算容量,要存放数据的最小的目标容量。
private static int calculateCapacity(Object[] elementData, int minCapacity) {
		// 如果传入的是个空数组 则 最小容量取默认容量与minCapacity之中的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 
           //DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
// ensureExplicitCapacity 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // 最小需要空间比elementData的内存空间更大,则需要扩容。
        if (minCapacity - elementData.length > 0)
        //扩容
            grow(minCapacity);
    }

// 扩容关键方法
    private void grow(int minCapacity) {
        // 获取ArrayList中的elementData数组的内存空间长度
        int oldCapacity = elementData.length;
        // 扩容为原来的1.5倍 验证下为什么是1.5倍,假设oldCapacity =5,则newCapacity =5+2=7 是1.4444倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 判断新数组容量够不够,够了直接使用这个长度创建新数组。
        // 不够就直接将数组长度设置为需要的长度
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
           // 若预设值大于默认的最大值 检测是否溢出
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyof方法将elementData数组指向新的内存空间是newCapacity的连续空间
        // 并将elementData的数组复制到新的内存空间
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

扩展

  • 知识点1 > 和 >> 和 >>> 的区别?> 表示大于;>> 表示右移,案例int i = 15,则i>>2 之后 i=3 分析:15的二进制00001111,右移两位就是00000011;>>> 进行无符号的位移处理,它不会将所处理的值的最高位视为正负符号,移位处理时直接空出来的高位补0。

ArrayList和LinkedList的区别?

区别有两点:

  1. 底层实现不同:ArrayList采用动态数据形式;LinkedList采用双向链表的形式
  2. 特点不同:ArrayList查找快、增删慢;LinkedList查找慢、增删快;

理解

  • 查询 ArrayList快 LinkedList慢。
    因为ArrayList可以使用下标直接索引对应的元素,而LinkedList需要一个个遍历。
  • 增删 ArrayList慢 LinkedList快
    非末项添加的情况,因为增加元素,ArrayList需要移动数组内对象的位置。

ArrayList增删不一定比LinkedList慢

  • 假如增删都在末尾操作,LinkedList使用的是add/offer和remove/poll,此时的ArrayList则不需要移动和复制数组来进行操作。当数据是百万级时,ArrayList速度会比LinkedList快;
  • 删除的数据在中间位置,LinkedList主要费时在遍历上,ArrayList主要费时在移动和删除上,当数据是百万级时,ArrayList会快于LinkedList。

ArrayList实现RandomAccess接口有何作用?为何LinkedList却没有实现这个接口?

ArrayList遍历使用for循环、LinkedList遍历使用迭代器
ArrayList使用for循环比使用迭代器遍历更快,而LinkedList使用迭代器比使用for循环遍历更快。

RandomAccess接口是一个标志接口,实现这个接口的类,都支持快速随机访问,也就是for循环的形式遍历元素;
而ArrayList实现该接口,可以提升它的遍历效率;

Array和ArrayList有和区别?什么更适合用Array?

  1. 概念不同:
    Array是编程基础组件;ArrayList是java集合中的一个实现类;
  2. 长度不同:
    Array长度固定,创建是设置好的,通过length属性可以得到长度;ArrayList长度不固定,初始长度是10,每次扩容1.5倍,通过size()查看长度;
  3. 存储对象不同:
    Array基本数据类型和对象;ArrayList对象;
  4. 泛型:
    Array不支持泛型;ArrayList支持泛型;
  5. 遍历方式不同:
    Array可以采用简单for循环遍历;ArrayList遍历方式有:简单for循环、增强for循环、迭代器;

Array的使用场景

  • 列表的大小已经指定,操作集中于存储和遍历元素;
  • 遍历基本数据类型;(虽然Collections有自动装箱操作,将基本数据类型变成其包装类,但是对于长度固定的基本数据类型数据来说,采用Array数组存储比ArrayList更便捷);
  • 想要多维数组,使用[][] 比使用List<<>>更容易。

Iterator怎么使用?有什么特点?

特点
是一个对象,可以遍历并选择序列中的对象;
通常被称为“轻量级”对象,因为创建它的代价小,功能简单,只可以单向移动;

使用方法

方法名说明
Iterator<?> iterator()返回一个Iterator对象
Collection集合对象所拥有的方法
E next()返回序列的下一个元素
若第一次调用,则返回序列的第一个元素
boolean hasNext()检查序列中是否有元素
default void remove()删除迭代器新返回的元素

Iterator和ListIterator有什么区别?

  1. 遍历对象不同:
    Iterator 可以遍历Set和List集合;ListIterator 只可以遍历List集合;
  2. 遍历形式不同:
    Iterator只能前向遍历;ListIterator可以前向、后向遍历;
  3. 共有方法:
    next、hasnext、remove;
  4. ListItrator特有方法:
    previous、hasPrevious、nextIndex、previousIndex、set;
    E previous() 返回列表中的上一个元素
    Boolean hasPrevious() 列表迭代器在相反的方向具有更多元素,返回true
    int nextIndex() 返回由后续调用返回的元素的索引值,也就是返回next()方法返回值的索引
    int previousIndex()返回由后续调用返回的元素的索引值,即返回previous()方法返回值的索引
    void set(E e) 用指定的元素替换next()或previous()返回的最后一个元素

Iterator和Enumeration接口的区别?

  1. 概念不同:
    Iterator是迭代器,用于实现Colleciton集合元素的遍历;
    Enumeration实现枚举类型数据的生成,一次生成一个;
  2. 接口方法不同:
    Iterator有三个方法,分别是next、hasnext、remove;
    Enumeration有两个方法,分别是hasMoreElements、nextElements;
  3. 安全性:
    Iterator更安全,因为实现了fail-fast机制,不允许在迭代过程中修改元素;
    Enumeration不安全;

ArrayList、Vector、LinkedList的区别?

  1. 底层实现不同:
    ArrayList和Vector采用动态数组的形式,LinkedList采用双向链表的形式;
  2. 线程安全性不同:
    ArrayList线程不安全,Vector和LinkedList线程安全;
  3. 访问形式不同:
    ArrayList和Vector可以随机访问,LinkedList只能顺序访问;

Vector线程安全的原因
java底层针对Vector线程不安全的操作都加了synchronized;
ArrayList线程不安全的原因
ArrayList的add方法中有size++的操作,该操作不是原子性的,当前a线程操作该集合进行size++的过程中,另一个线程b也对该集合进行操作,那么此时该集合对象的size就不准确了;

Set集合

HashSet的实现原理、以及如何保证元素不重复?

底层通过HashMap实现;
HashSet构造方法创建对象的时候,底层会创建一个HashMap对象。
HashSet集合存储的元素会存放在HashMap集合的key键位置。由于HashMap的key具有唯一性,因而HashSet存储的元素具有唯一性。
底层HashMap中value的位置存储是present(仅是一个占位符)。

Set用什么方法来区分重复与否呢?

以HashSet为例。

  1. HashSet集合,底层的实现是基于 HashMap,HashSet的值 存放在 HashMap的key 的位置,由于Key具有唯一性,那么HashSet的元素就不能重复。
  2. HashMap的key 唯一性如何保证。
    当执行put(key,value)操作的时候,①通过hashCode()计算key对应的hash值 ②计算index = hash &(length-1)③判断哈希表index位置是否有元素,有元素则比较hash值是否相同 ④ 哈希值不相同 则存储成功;相同 则继续比较key ⑤如果key不同 则存储成功;相同,则会替换原本的value。

整体的比较过程是:先计算hash值、再计算index索引、然后查看索引位置、有元素比较哈希值、不相同存储、相同再比较key值,不相同存储、相同将其替换

Queue集合

Map集合

LinkedHashMap的实现原理?

LinkedHashMap是通过 哈希表 和 链表 实现的。
它是基于HashMap实现的,不同之处是定义了一个Entry header(头指针) 和 Entry tail(尾指针),不在table中,独立于table。
继承了HashMap的Entry,并添加了两个属性 before 和 after,before、after、Entry header组成一个链表,实现按插入顺序或访问顺序排序。此时Entry元素保存的有:当前对象的引用、上一个元素before引用、下一个元素after的引用。
当前对象的引用指的是 Value?
面试3LinkedHashMap

& HashMap的实现原理/底层数据结构?JDK1.7和JDK1.8

区别

  1. 底层结构不同:
    JDK1.7 = 采用数组+链表的形式;JDK1.8 = 采用数组+链表/红黑树的形式;
    链表什么情况会变成红黑树:当某个索引位置上以链表形式存在的元素的存储个数>8,并且当前数组的长度>64时候,此索引位置上链表会变成红黑树;
  2. 新元素添加方式不同:
    JDK1.7使用新元素指向旧元素,头插法;JDK1.8是旧元素指向新元素,尾插法;
  3. 实例化不同
    创建数组的长度都是16,JDK1.7底层创建Entry[] table;JDK1.8底层创建Node[]

实例化
HashMap hm = new HashMap(); 【以JDK1.7为例】

  • 底层创建长度是16的一维数组Entry[] table
  • 执行hm.put(key1,values1);
  • 调用类的hashCode计算key1的哈希值,再利用哈希值计算index索引,索引值为0-15之间,映射到Entry数组;
    (计算过程是:index = hashcode & (length -1))
  • 若位置为空,则(key1、value1)添加成功;
  • 若位置不为空,比较key1和已经存在的数据 比较其哈希值
    • 都不同,则(key1、value1)添加成功;
    • 存在相同的,则进一步通过equals比较key
      • 返回值是false,(key1、value1)添加成功;
      • 返回值是true,就是value1替换已存在的值。

HashMap的put方法的执行过程?

  • 创建HashMap对象后,底层会产生一个长度为16的entry[]数组。
    put方法的功能是向HashMap集合中添加键值对元素。put(key,value)
  • 根据key计算对应的hash值,根据hash值确定在链表中的位置;【本质哈希值%16取余确定位置,但具体的实现是通过index = hash & (数组.length-1)】
  • 该位置没有元素,则成功添加;
  • 有元素,则需要将此元素hash值与位置中存储元素的hash值进行比较。
    • 没有一个相同,则成功添加;
    • 存在相同,则比较具体的key
      • 相同,则用新的value覆盖旧的value;
      • 不同,则成功添加。

HashMap的get方法的执行过程?

hash值、index、判断是否为空-是否是要找的值、再判断第二个点不为空判断数据类型、链表采用equals、红黑树采用getTreeNode查找

  • 根据hash方法获得key的hash值
  • 通过hash&(length-1)的方式获得key对应的Entry[]数组下标。
    hash&(length-1) 替换的是 hash % length 的操作 hash=25(11001) length=16 计算结果是满足的。
  • 每次都会判断结点是否为空,不为空则判断是否是要找的值,不是则继续判断,是则返回null
  • 判断第二个结点是否为空,为空则返回null,不为空则判断数据结构是链表还是红黑树
    • 链表则进行顺序遍历,使用==和equals判断key是否相同,满足条件则返回,遍历完都没有找到返回null
    • 红黑树结构,使用getTreeNode()查找操作。

HashMap的resize方法的执行过程?

  1. 基础:
    HashMap初始大小是16,扩容采用2倍的形式;

  2. 调用resize方法的情况:
    第一次调用HashMap的put方法的时候,会调用resize方法对table数组进行初始化,不传入指定值,默认大小是16;
    扩容时调用resize,即size>threshold时,table数组大小会翻倍;

  3. resize方法触发时机:
    初始化HashMap的默认扩容是cap=16,threshold=12, 的Node<K,V>[] newTab
    当hashMap的size>threshold的时候,再次扩容,容量cap=162,阈值threshold=122
    当table中的Node链表>8,且tab.length<64,hash再次double扩容;

JDK1.8之后,HashMap头插法改为尾插法?

修改原因是:

  • 并发情况下,头插法可能会形成数据环,get数据时死循环;
  • 为了代码书写方便:jdk1.7链表结构,采用头插法更方便;jdk1.8红黑树结构,采用尾插法更方便;

HashMap的size为什么必须是2的整数次方?

计算速度快、内存空间浪费少
优点:

  • 能保证HashMap的底层数组长度是2的n次方,此条件满足的话,hash % length 操作可以被 替换成 hash &(length-1),后者速度比直接取模速度快;计算速度
  • length是2的幂次,length-1必定是11111…的形式,与hash进行&操作效率更高,且空间没有浪费。
    解释:如果length=15,那么length-1 = 14 》1110 则与hash&操作之后,最后一位都是0,那么0001、0011、0101、0111 、1001 、1011、1101、1111这几个位置永远不会放元素。内存空间浪费

HashMap多线程死循环问题?

当多线程同时put时,且同时触发扩容操作,会导致HashMap中的链表中出现循环节点,进而使得get的时候,出现死循环。

描述:
链表存储key分别是3 7 5 由于hash表的size是2,引起扩容4,之后的存储就是index0123 对应的是5 73
上述是单线程的情况,要是多线程会存在循环的情况;
线程1想要扩容的时候,突然被挂起了;但是线程1的e指向key3;
线程2开始扩容并且已经完成,具体的存储是index0123 对应的是5 73;
线程1被唤醒之后,会执行e = next 也就是e = e.next 此时指向了key 7 ;
接下来再取元素key 3放在index 3的链表头部,此时就会产生head→key 3→key 7→key 3的情况。

在这里插入图片描述

HashMap的get方法可以判断某个元素在map中?

不能判断。
因为HashMap中允许key、value为null。
通过get方法获得value为null,不能确定是value是null,还是key不存在于HashMap集合中。

HashMap与HashTable的区别是什么?

  1. 父类不同:
    HashMap父类是AbstactMap;HashTable父类是Dictionary;
  2. key、value不同:
    HashMap允许key、value为null;HashTable不允许key、value为null;
  3. 线程安全性:
    HashMap线程不安全、HashTable线程安全;
    安全性采用的是锁机制,使用synchronized锁住整张表;
  4. 底层数组不同:
    HashMap底层数组长度是16、扩容是2倍、index计算方式是hash & (tab.legnth-1);
    HashTable底层数据长度是11、扩容是old*2+1,index 的计算方式是 (hash & 0x7FFFFFFF) % tab.length

HashMap与ConcurrentHashMap的区别是什么?

  1. 线程安全:
    HashMap线程不安全、ConcurrentHashMap线程安全;
  2. 并发扩容:
    HashMap不支持、ConcurrentHashMap通过锁来实现并发扩容;
    采用分段锁,将整个hash桶进行分段segment,即将底层数组分段segment,每个小段都加锁,插入元素需要考虑插入哪一段,并且需要获得对应segment段的锁,这样减少了锁的粒度。

HashTable与ConcurrentHashMap的区别是什么?

  1. 效率问题:
    HashTable效率低:使用synchronized关键字对操作进行加锁,且锁的是整张Hash表,线程只可以单独运行;
    ConcurrentHashMap效率高:内存分段,给每个段加锁,执行操作的时候会先判断是哪个Segment,然后再去获取对应的锁,实现了多线程并发编程。

ConcurrentHashMap的实现原理是什么?

JDK1.7
实现形式:HashEntry(数组) + Segment(分段锁) 的方式
将数据分成一段一段存储,给每一段都分配一个锁,当线程占用锁访问其中一个数据段的时候,其余数据段可供其他线程访问。

  • 采用ConcurrentHashMap存储数据,数据区被分成多个Segment(即Segment数组),每个Segment都会被分配锁。
  • Segment是数组和链表结构,每个Segment包含一个HashEntry数组,HashEntry中的每个元素都是链表结构的,Segment守护所包含的HashEntry数组里的元素,当对HashEntry数组的元素进行修改的时候,必须先获得Segment锁。
  • ConcurrentHashMap有两个内部静态类:HashEntry用来封装映射表的键值对;Segment用来表示锁。
  • Segment是一种可重入锁(ReentrantLock),它是Lock接口的子类。

JDK1.8
实现形式:Node + CAS + Synchronized 来保证并发安全进行实现。(也有说是 数组 + 链表 + 红黑树)
底层是Node类型的数组,引入CAS机制避免加锁操作,仍会出现同步代码,锁的粒度相对于分段锁而言更加细粒度。

CAS = Compare And Swap,比较并替换。常用的三个操作数:内存地址V、旧的预期值A、要修改的新值B
工作原理:更新一个变量,只有当变量的预期值A和内存中的实际值相同的时候,才会将内存地址的值改为新值B。
java中的原子类的底层利用了CAS机制。

其他

不同数据类型获取长度的方法?

数据类型获取长度的方式
数组数组.length属性
StringString.length()方法
集合集合.size() 方法
collection集合的方法
文件文件.length()方法

遗留的集合有哪些?

HashTable、Stack、Vector。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值