JAVA集合学习

一、结构

List和Set继承了Collection接口,Collection继承了Iterable

Object类是所有类的根类,包括集合类,集合类中的元素通常是对象,继承了Object类中的一些基本方法,例如toString()equals()hashCode()

Collection的增强for底层就是简化版本的迭代器遍历,可以DEBUG看到过程

对集合的遍历:list - 删除元素 ConcurrentModificationException-CSDN博客

Object常用方法

  • toString():返回对象的字符串表示形式。

  • equals(Object obj):判断两个对象是否相等。

  • hashCode():返回对象的哈希码值。

  • getClass():返回对象的运行时类。

  • clone():创建并返回此对象的副本。

  • finalize(): 该方法用于释放资源。

  • wait():睡眠

  • notify():唤醒

二、List

1、ArrayList

  • 线程不安全

  • 有序(添加和取出顺序一致),可重复,可以添加多个null

  • 默认大小是10,扩容是1.5倍

  • 每个元素都有对应的顺序索引,即支持索引。get()体现。

常用方法

  • add()/addAll()

  • set()

  • get()

  • remove()

  • size()

  • isEmpty()

  • indexOf()

  • contains()

  • replaceAll()

  • subList()

底层

  • ArrayList维护一个Object类型的数组elementData,就是一个空数组

  • 当创建ArrayList对象时,如果使用的是无参构造器,初始容量是0,第一次添加扩容到10,如果再扩容是1.5倍,用 Arrays.copyOf 方法旧数组数据拷贝进去,再添加新的进去

  • 如果是指定大小的构造器,初始是指定大小,再次扩容是1.5倍

transient避免序列化

(1)无参构造

确认最小容量,赋值为10

modCount记录集合被修改的次数,此时minCapacity是10,但是elementData是空,就需要扩容

向右移动一位,就是除以2,比如初始化是个空数组,那么在newCapacity的时候还是0,所以第一次扩容不是直接扩,是在第一个if才扩容赋值为10,copyOf后都是null

(2)有参构造

同上

2、Vector

  • 线程安全的,操作方法带有synchronized

  • 默认10,扩容是2倍

底层

(1)扩容

确定是否需要扩容

int类型初始化是0,所以newCapacity是两倍

(2)克隆

实现了Cloneable接口

调用 Object 类的 clone 方法,创建一个浅拷贝,对于数组元素,使用 Arrays.copyOf 方法进行深拷贝,重置副本的修改次数

3、LinkedList

  • 线程不安全

  • 双向链表和双端队列

  • 添加元素可重复,可添加null

LinkedList维护了两个属性first和last指向首节点和尾节点,每个节点对象,又维护了prev、next、itm三个属性,通过prev指向前一个,next指向后一个节点,实现双向链表。

常用方法

【带有first和last就是实现了Deque,常用的是实现Queue】

add(E e):实现了List接口。在链表后添加一个元素;

addFirst(E e)/push(E e)、addLast(E e):实现了Deque接口。在链表头部/尾部插入一个元素;

offerFirst(E e)、offerLast(E e):实现了Deque接口。在链表头部插入一个元素,成功返回true,失败false

offer(E e):实现了Queue接口。将指定的元素插入到队列的末尾,成功返回true,失败false

peek():获取第一个元素,但是不移除;

poll():查询并移除第一个元素 ;

remove() :移除链表中第一个元素;

底层:

(1)add

first = null, last = null

第一个节点加入的时候,first和last都指向newNode,prev=next=null

再加入一个节点, l指向第一个节点的last,再将l赋值到newNode的prev,将第一个节点的last指向newNode,此时l指向的是第一个节点不为空,将l的next指向newNode,完成链表,size=modCount=2

(2)removeFirst

4、比较

ArrayListLinkedListVector
底层结构可变数组双向链表可变数组
线程安全不安全(Collections.synchronizedList)不安全安全(synchronized)
初始值10,扩容1.5倍10,扩容2倍空链表
排序重复有序,可重复,多个null有序,可重复,多个null有序,可重复,多个null
效率增删较低,查找快(索引)增删较高,查找慢(顺序遍历)
适用大部分频繁插入和删除

2、拓展Collections.synchronizedList

Collections.synchronizedList方法接受一个普通的List作为参数,并返回一个具有同步方法的List。可以使用这个同步的List在多线程环境中进行操作。尽管synchronizedList提供了一些基本的同步,但在复合操作(例如迭代和修改)时,仍然需要手动同步以确保线程安全。

https://www.cnblogs.com/jiading/articles/13038607.html

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
​
public class ManualSynchronizationExample {public static void main(String[] args) {// 创建一个普通的ArrayListList<String> normalList = new ArrayList<>();
​// 使用Collections.synchronizedList方法创建线程安全的ListList<String> synchronizedList = Collections.synchronizedList(normalList);
​// 创建两个线程,一个线程向列表中添加元素,另一个线程遍历列表Thread addThread = new Thread(() -> {for (int i = 0; i < 1000; i++) {addElement(synchronizedList, "Element" + i);}});
​Thread iterateThread = new Thread(() -> {iterateList(synchronizedList);});
​// 启动线程addThread.start();iterateThread.start();
​// 等待两个线程执行完成try {addThread.join();iterateThread.join();} catch (InterruptedException e) {e.printStackTrace();}}
​// 使用 synchronized 关键字确保对列表的安全添加操作private synchronized static void addElement(List<String> list, String element) {list.add(element);}
​// 使用 synchronized 关键字确保对列表的安全遍历操作private synchronized static void iterateList(List<String> list) {for (String element : list) {// 在迭代过程中对列表进行操作System.out.println(element);}}
}
​
//synchronizedList里的synchronized内部锁,是在并发执行的add/remove的时候,
//不要把多个线程操作的数据实现到一个地方去。
//外部再使用synchronized是保证添加和遍历的时候对线程的安全的保护,两个锁是不同层面的并发问题

三、Set

1、HashSet

  • 底层是实现的HashMap,数组+链表+红黑二叉树,线程不安全

  • 默认大小16,加载因子0.75,扩容到原先的2倍

  • 无序,不允许重复,最多一个null、

(1)add

Set s = new HashSet();
set.add("a");                  //true
set.add("a");                  //false
set.add(new Student("lee"));    //true
set.add(new Student("lee"));    //true
set.add(new String("lee"));     //true
set.add(new String("lee"));     //false

如果put返回的是null,相当于是没有这个e,那么才返回true,否则代表已经存在

这里的PRESENT是个占位的目的,也就是add(“a”),这个a是在key上的,value是个空对象

右移16位并且异或,核心目的是为了让hash值的散列度更高,减少hash冲突,将hashCode的高位移动到了低位,使低位包含更多的高位信息

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//定义辅助变量,table就是HashMap的一个数组,类型是Node[]Node<K,V>[] tab; Node<K,V> p; int n, i;//看下个代码解析,计算如果当前tab是null或者大小是0,此时的tab大小就是16了if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//计算hash值,应该放在table表的哪个索引位置,并且这个位置的对象赋值给p//如果p是null,表示还没有创建过数据,就放进去//绝大部分情况下,n的值小于2的16次方,所以i的值始终是hash的低16位和n-1取模运算,造成key的散列度不高,大量的key集中存储几个位置,影响性能if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//相同的数据,例如第一次加入A,第二次加入ANode<K,V> e; K k;//p是指向当前索引位置对应的第一个元素,如果和添加的key的hash值一样,并且添加的key是同一个对象,或者不是同一个对象但是内容相同。代表两个数据是相同的,不能加入if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;//p是否是红黑树,走红黑树的算法else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {//是一个链表,for循环比较for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);//添加后立即判断链表的长度是否达到8,转为红黑树if (binCount >= TREEIFY_THRESHOLD - 1)treeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}//如果已经存在相同的k,但是v不同,那么就会替换vif (e != null) {V oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();//这个是为了让HashMap的子类去实现的afterNodeInsertion(evict);return null;}
 
final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1;}else if (oldThr > 0)newCap = oldThr;else {//static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 也就是默认16newCap = DEFAULT_INITIAL_CAPACITY;//默认加载因子0.75,使用到12的时候就要扩容newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;@SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//此时的table的大小就是16了table = newTab; if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else {Node<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;}

2、LinkedHashSet

  • 继承了HashSet,底层是LinkedHashMap,维护的是 数组+双向链表

  • 默认大小是16,扩容为2倍

  • 数组是HashMap$Node[],存放节点类型是LinkedHashMap$Entry

继承关系是在静态内部类完成的

同上

3、TreeSet

  • 当无参构造的时候是无序的,根据提供的构造器的比较器可以完成有序

  • 底层实现TreeMap

TreeSet treeSet = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {//比较方式return 0;}
});

添加元素时,cpr就是匿名内部类,compare()动态绑定到匿名内部类

第一次添加的时候,比较的是相同的key不接收返回值,所以添加第一个元素时不影响

4、区别

HashSetLinkedHashSetTreeSet
底层HashMapHashSet+链表TreeMap+红黑树
是否有序无序有序无参无序,构造器有序
性能O(1),速度快O(1),链表有影响O(log n),性能较差

四、Map

1、HashMap

  • key不能重复,值可以重复,允许为null唯一

  • 相同的k,会覆盖原来的kv

  • 线程不安全

  • 最大容量Integer.MAX_VALUE,2 的 31 次方-1

  • 负载因子0.75,扩容为2倍

  • 底层=数组+链表+红黑二叉树

(1)put()同理set集合

(2)遍历

存有的K-V结构的数据,一对k-v是放在一个HashMap$Node中的,Node实现了Exntry接口,一对k-v就是一个Entry。

返回是一个HashMap$Node

k-v为了方便遍历,还会创建EntrySet集合,存放的元素类型Entry,而一个Entry对象就有k-v

transient Set<Map.Entry<K,V>> entrySet;

这个entrySet保存的kv只是指向了Node的kv

entrySet中定义的是Map.Entry, 但实际上存放的是HashMap$Node,因为后者implements前者

提供了常用方法

  • getKey()

  • getValue()

  • Set set = map.keySet();

  • Collection values = map.values();

(3)1.7和1.8的区别

JDK1.7JDK1.8
底层entry数组+链表node数组+链表+红黑树(链表长度>8 && 元素数量>64)
扩容全部按照之前的方法进行重新计算索引按照扩容后的规律计算(扩容后的位置=原位置or原位置+旧容量)
计算hash4次位运算 + 5次异或预算1次位运算+1次异或运算
初始容量0(第一次执行put初始)16(put时初始)
插入头插法(造成死循环)尾插法

(4)问题

1.8后扩容后的规律计算?

扩容后,若hash值新增参与运算的位=0,那么元素在扩容后的位置=原始位置;

扩容后,若hash值新增参与运算的位=1,那么元素在扩容后的位置 =原始位置+扩容后的旧位置;

为什么链表的长度大于8就要转换成红黑二叉树?

最开始使用链表的时候,空间占用较少,对查询速度没有太大的影响,当链表越来越长,就转换为红黑二叉树,保证查询速度。红黑二叉树是平衡二叉树,也避免不平衡二叉树偏向一边导致查询速度慢的问题。

如果 hashCode 分布良好,那么红黑二叉树几乎不会用到,很少出现链表很长的情况。在理想情况下,链表长度符合泊松分布,各个长度的命中概率依次递减,当长度为 8 的时候,概率仅为 0.00000006。因此默认8为转换的阈值。

为什么要右移16位异或?

绝大部分情况下,n的值小于2的16次方,所以i的值始终是hash的低16位和n-1取模运算,造成key的散列度不高,大量的key集中存储几个位置,影响性能。右移16位并且异或,将hashCode的高位移动到了低位,使低位包含更多的高位信息

加载因子为什么是0.75?

扩容因子值越大,触发扩容的元素个数越多,虽然空间利用率高,但是哈希冲突概率增加。

扩容因子值越小,触发扩容的元素个数越少,哈希冲突概率减少,但是占用内存,增加扩容频率。

扩容因子的值的设置,是在统计学中泊松分布有关,再冲突和空间利用率之间的平衡

头插法到尾插法的优化是为什么?

只会在1.7中出现,1.7采用的是数组+链表且是头插法。当有两个线程TA和TB,对HashMap进行扩容,都指向链表头结点NA,而TA和TB的next都是NB,假设TB休眠,TA执行扩容操作,一直到TA完成后,TB被唤醒,TB.next还是NB,但是TA执行成功已经发生了顺序改变,出现一个死循环。

为什么数组容量必须是2次幂? 桶的索引计算公式为 i = (n-1) & hash。效果等于与 hash%n 的计算效果,但是位运算比取余运算要高效的多。如果n为2次幂,那么n-1的低位就全是1,哈希值进行与操作时可以保证低位的值不变,从而保证分布均匀,不会受到与运算对数据的变化影响。

2、HashTable(不使用)

  • k-v都不能为空,否则抛出NullPointException

  • 线程安全

  • 底层是HashTable$Entry[],初始化大小11,扩容是2倍+1

3、TreeMap

同TreeSet

4、ConCurrentHashMap

1.71.8
锁结构ReentrantLock+Segment+HashEntrysynchronized+CAS+红黑树
锁力度Segment节点
size()调用的时候才计算。不加锁直接进行统计,最多执行三次,如果前后两次计算的结果一样,则直接返回。若超过了三次,则对每一个Segment进行加锁后再统计已经计算好的预估值。维护一个baseCount属性用来记录结点数量,每次进行put操作之后都会CAS自增baseCount
put()两次定位,先对Segment定位,再对内部数组下标定位,定位不到会使用自旋锁加锁,自旋超过64次进入阻塞状态一次定位,CAS+synchronized,如果对应下标没有结点,则没有哈希冲突,直接CAS插入,成功返回,不成功使用synchronized加锁插入
get()一次hash定位到Segment的位置,然后再hash定位到指定的HashEntry,遍历该HashEntry下的链表使用CAS保证并发安全,因为Node数组是共享的。根据哈希定位到桶,为空返回null,怒为空查找链表/红黑树

1.8为什么使用synchronized来代替重入锁ReentrantLock?

因为粒度降低了,在相对而言的低粒度加锁方式,synchronized并不比ReentrantLock差,在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,更加的灵活,而在低粒度中,Condition的优势就没有了。 synchronized在1.8之后,JVM 使用了锁升级的优化方式,先使用偏向锁尝试获取锁,没有获取到代表锁已经偏向其他线程,升级到自旋锁,达到一定次数后,才升级到重量级锁。

为什么get不加锁可以保证线程安全?

整个get方法使用了大量的volatile关键字,其实就是保证了可见性,get只是读取操作,所以只需要保证读取的是最新的数据。Node的成员val是用volatile修饰,数组用volatile修饰保证数组扩容时的可见性。

使用volatile关键字会强制将修改的值立即写入主存,使用volatile关键字,当线程2进行修改时,会导致线程1的工作内存中缓存变量的缓存行无效,由于线程1的工作内存中缓存变量的缓存行无效,所以线程1再次读取变量的值时会去主存读取。volatile就是强制多线程的情况下读取数据区内存获取最新的值。

五、Collections工具类

  • reverse(List list):反转指定List集合中元素的顺序

  • shuffle(List list):对List中的元素进行随机排序(洗牌)

  • sort(List list):对List里的元素根据自然升序排序

  • sort(List list, Comparator c):自定义比较器进行排序

  • swap(List list, int i, int j):将指定List集合中i处元素和j出元素进行交换

  • rotate(List list, int distance):将所有元素向右移位指定长度

  • binarySearch(List list, Object key):使用二分搜索法,以获得指定对象在List中的索引,前提是集合已经排序

  • max(Collection coll):返回最大元素

  • max(Collection coll, Comparator comp):根据自定义比较器,返回最大元素

  • min(Collection coll):返回最小元素

  • min(Collection coll, Comparator comp):根据自定义比较器,返回最小元素

  • fill(List list, Object obj):使用指定对象填充

  • frequency(Collection Object o):返回指定集合中指定对象出现的次数

  • replaceAll(List list, Object old, Object new):替换

  • emptyXxx():返回一个空的不可变的集合对象

  • singletonXxx():返回一个只包含指定对象的,不可变的集合对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/191793.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战Leetcode(五)

Practice makes perfect&#xff01; 实战一&#xff1a; 思路&#xff1a;我们要用复制的节点来组成一个新的链表&#xff0c;而原链表的节点随机指向其中一个节点&#xff0c;我们首先给每一个节点都复制并且插入到原来节点的后面&#xff0c;然后用复制的节点指向我们原来节…

物理问题中常见的分析问题----什么样的函数性质较好

物理问题中常见的积分符号位置交换问题 重极限与累次极限 高数下的定义 累次极限&#xff1a;求极限时需要遵循一定的顺序重极限&#xff1a;任意方向趋于的极限 两者之间的关系&#xff1a; 两者没啥关系存在累次极限存在而不相等的函数...... 求和符号与积分符号互换--逐项积…

RK3568笔记五:基于Yolov5的训练及部署

若该文为原创文章&#xff0c;转载请注明原文出处。 一. 部署概述 环境&#xff1a;Ubuntu20.04、python3.8 芯片&#xff1a;RK3568 芯片系统&#xff1a;buildroot 开发板&#xff1a;ATK-DLRK3568 开发主要参考文档&#xff1a;《Rockchip_Quick_Start_RKNN_Toolkit2_C…

专题知识点-二叉树-(非常有意义的一篇文章)

这里写目录标题 二叉树的基础知识知识点一(二叉树性质 )树与二叉树的相互转换二叉树的遍历层次优先遍历树的深度和广度优先遍历中序线索二叉树二叉树相关遍历代码顺序存储和链式存储二叉树的遍历二叉树的相关例题左右两边表达式求值求树的深度找数找第k个数二叉树非递归遍历代码…

C++ builder 常见问题汇总

1、CB静态编译设置 2、CB10.3设置经典编译器&#xff08;用于解决10.3弹出代码提示慢&#xff09; 3、CBuilder生成Release版本 &#xff1a; project->Options->CCompiler->Build Configuration 选择 Release project->Options->CLinker中取消Use dynamic RTL…

简单实现,在nodejs中简单使用kafka

什么是 Kafka Kafka 是由 Linkedin 公司开发的&#xff0c;它是一个分布式的&#xff0c;支持多分区、多副本&#xff0c;基于 Zookeeper 的分布式消息流平台&#xff0c;它同时也是一款开源的基于发布订阅模式的消息引擎系统。 Kafka 的基本术语 消息&#xff1a;Kafka 中的…

深入理解 Django 单元测试

概要 在现代软件开发流程中&#xff0c;单元测试是确保代码质量和可维护性的关键组成部分。对于使用 Django 框架的项目来说&#xff0c;Django 提供了一套强大的测试工具来帮助开发者编写和运行单元测试。本文将深入探讨 Django 中的单元测试&#xff0c;包括测试原理、编写测…

vue3 ref 与shallowRef reactive与shallowReactive

ref 给数据添加响应式&#xff0c;基本类型采用object.defineProperty进行数据劫持&#xff0c;对象类型是借助reactive 实现响应式&#xff0c;采用proxy 实现数据劫持&#xff0c;利用reflect进行源数据的操作 let country ref({count:20,names:[河南,山东,陕西],objs:{key…

19C进入数据库出现问号

问题情况如图所示&#xff1a; 解决方法&#xff1a; su - oracle echo "NLS_LANGAMERICAN_AMERICA.ZHS16GBK;export NLS_LANG" >> ~/.bash_profilesource ~/.bash_profileofile

《网络协议》05. 网络通信安全 · 密码技术

title: 《网络协议》05. 网络通信安全 密码技术 date: 2022-09-10 15:16:15 updated: 2023-11-12 07:03:52 categories: 学习记录&#xff1a;网络协议 excerpt: 网络通信安全&#xff08;ARP 欺骗&#xff0c;DoS & DDoS&#xff0c;SYN 洪水攻击&#xff0c;LAND 攻击&a…

activiti7审批驳回,控制变量无法覆盖,导致无限循环驳回,流程无法结束

项目开发过程中使用工作流&#xff0c;因此考虑使用activiti7做完工作流引擎。项目开发过程中&#xff0c;发现流程驳回时&#xff0c;再次执行流程&#xff0c;控制变量无法覆盖&#xff0c;导致无限循环驳回&#xff0c;流程无法结束。流程图如下图所示&#xff1a; 驳回控制…

数据结构 栈(C语言实现)

目录 1.栈的概念及结构2.栈的代码实现 1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In F…

【JavaEE】Servlet API 详解(HttpServletRequest类)

二、HttpServletRequest Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符串解析成 HttpServletRequest 对象&#xff08;内容和HTTP请求报文一样&#xff09; 1.1 HttpServletRequest核心方法 1.2 方法演示 WebServlet("/showRequest&…

线性代数理解笔记

一.向量引入: 向量&#xff1a;只由大小和方向决定&#xff0c;不由位置决定。 二.向量加减法 向量的加法是首尾相连&#xff0c;减法是尾尾相连。 而向量v向量w为平行四边形主对角线。 向量v-向量w为平行四边形副对角线。 2.向量内积点乘&#xff08;内积&#xff09; 内积…

谈谈如何沟通

序言 如果你是对的&#xff0c;就要试着温和地、技巧地让对方同意你&#xff1b;如果你错了&#xff0c;就要迅速热忱地承认。这比为自己争辩有效和有趣的多。——卡耐基【美】 通过上篇文章谈谈如何写作&#xff08;一&#xff09;&#xff0c;我们了解了如何表达的一些基本的…

国际阿里云:云服务器灾备方案!!!

保障企业业务稳定、IT系统功能正常、数据安全十分重要&#xff0c;可以同时保障数据备份与系统、应用容灾的灾备解决方案应势而生&#xff0c;且发展迅速。ECS可使用快照、镜像进行备份。 灾备设计 快照备份 阿里云ECS可使用快照进行系统盘、数据盘的备份。目前&#xff0c;阿…

【10套模拟】【3】

关键字&#xff1a; 物理存储、完全二叉树、出栈入栈时间复杂度、线索二叉树

Vue基础必备掌握知识点-Vue的指令系统讲解(二)

Vue指令系统继续讲解 v-for 作用:基于数据进行循环&#xff0c;多次渲染整个元素 数据类型:数组.对象.数字。。。 遍历数组语法&#xff1a;v-for"(item,index)" in 数组 item:表示每一项 index:则是表现下标 注意:v-for中的key值&#xff0c;key属性唯一的…

MyBatis解析全局配置文件

MyBatis解析全局配置文件 MyBaits基础应用&#xff1a; 文档&#xff1a;MyBatis 链接&#xff1a;http://note.youdao.com/noteshare?id5d41fd41d970f1af9185ea2ec0647b64 传统JDBC和Mybatis相比的弊病 传统JDBC ​ Connection conn null; PreparedStatement pstmt …