Java集合提升
1. 手写ArrayList
1.1. ArrayList底层原理细节
- 底层结构是一个长度可以动态增长的数组(顺序表)
transient Object[] elementData;
- 特点:在内存中分配连续的空间,只存储数据,不存储地址信息。位置就隐含着地址。
- 优点
- 节省存储空间,因为分配给数据的存储单元全用于存放节点的数据(不考虑c/c++中数组需指定大小的情况),节点之间的逻辑关系没有占有额外的存储空间
- 索引查找效率高,即每一个节点对应一个序号,由该序号可以直接计算出来节点的存储地址。
- 缺点
- 插入和删除操作需要移动元素,效率较低
- 必须提前分配固定数量的空间,如果存储元素少,可能导致空闲浪费
- 按照内容查询效率低,因为需要逐个比较判断
- 通过无参构造方法创建对象时,JDK1.7初始长度时10;JDK1.8初始长度是0,在第一次添加元素时就进行扩容
- 容量不足时,每次扩容增加50%,
int newCapacity = oldCapacity + (oldCapacity >> 1);
- 提供一个内部类ltr,实现了Iterator接口。用来对ArrayList进行遍历
public Iterator<E> iterator() {return new itr();
}
private class itr implements Iterator<E> {
}
1.2. 定义List接口和Iterator接口
public interface Iterator<T> {boolean hasNext();T next();
}
public interface List {// 返回线性表的大小,即数据元素的个数public int size();// 返回线性表中序号为i的数据元素public Object get(int i);// 如果线性表为空返回true,否则返回falsepublic boolean isEmpty();// 判断线性表是否包含数据元素epublic boolean contains(Object e);// 返回数据元素e在线性表中的序号public int indexOf(Object e);// 将数据元素e插入到线性表中i号位置public void add(int i, Object e);// 将数据元素e插入到线性表末尾public void add(Object e);// 将数据元素e插入到元素obj之前public boolean addBefore(Object obj, Object e);// 将数据元素e插入到元素obj之后public boolean addAfter(Object obj, Object e);// 删除线性表中序号为i的元素并返回public Object remove(int i);// 删除线性表中第一个与e相同的元素public boolean remove(Object e);// 替换线性表中序号为i的数据元素为e,返回原数据元素public Object replace(int i, Object e);// 迭代Listpublic Iterator iterator();
}
1.3. 手写ArrayList
/*
* 自定义数组越界异常
* */
public class IndexOutOfBoundsException extends RuntimeException {public IndexOutOfBoundsException() {}public IndexOutOfBoundsException(String message) {super(message);}
}
public class ArrayList implements List {// 底层数组来存储多个元素private Object[] elementData;// 存储元素的个数,线性表的长度,不是数组长度private int size;public ArrayList() {// 查看真实的ArrayList无参数构造方法是怎么实现的this(10);// this.elementData = new Object[]();// this.elementData = new Object[0];}public ArrayList(int initialCapacity) {// 如果initialCapacity<0,抛出异常if(initialCapacity < 0) {throw new IndexOutOfBoundsException("初始长度要大于0: " + initialCapacity);}// 按照初始长度给数组分配空间elementData = new Object[initialCapacity];// 指定线性表初始元素个数,可以省略,int成员变量默认值0// this.size = 0;}@Overridepublic int size() {return size;}@Overridepublic Object get(int i) {// 对i值进行判断if(i >= size) {throw new IndexOutOfBoundsException("数组指针越界异常:" + i);}return elementData[i];}@Overridepublic boolean isEmpty() {return size == 0;}@Overridepublic boolean contains(Object e) {// 逐个判断各元素是否与要查询元素内容相同,效率低下// 注意不是elementData.length,而是size/*for (int i = 0; i < size; i++) {if(e.equals(elementData[i])) return true;}return false;*/return this.indexOf(e) >= 0;}@Overridepublic int indexOf(Object e) {// 逐个判断各个元素是否与要查询元素内容相同,效率低下// 有漏洞,如果e为null,使用if分别处理for (int i = 0; i < size; i++) {if(e.equals(elementData[i])) {return i;}}return -1;}@Overridepublic void add(int i, Object e) {// 如果i超过了size,抛出异常if(i > size || i < 0) {throw new IndexOutOfBoundsException("数组指针越界异常:" + i);}// 需要先判断length是否足够,如果已经满了,需要扩容if(size == this.elementData.length) {grow();}// 添加元素// 从后向前移后面元素一个位置for (int j = size; j > i; j--) {this.elementData[i] = this.elementData[j-1];}// 添加元素到指定位置this.elementData[i] = e;// 元素个数+1this.size++;}@Overridepublic void add(Object e) {// 需要先判断length是否足够,如果已经满了,需要扩容if(size == this.elementData.length) {grow();}// 增加元素this.elementData[size] = e;// 元素长度+1size++;// 可以合并成一条语句// this.elementData[size++] = e;}private void grow() {// 创建一个新的更长数组/*Object[] newArr = new Object[this.elementData.length * 2];// 将旧数组数据放入到新数组for (int i = 0; i < this.elementData.length; i++) {newArr[i] = this.elementData[i];}// 旧数组引用指向新数组this.elementData = newArr;*/elementData = Arrays.copyOf(elementData, this.elementData.length * 2);}@Overridepublic String toString() {if(this.size == 0) return "[]";StringBuilder builder = new StringBuilder();builder.append("[");for (int i = 0; i < size; i++) {if(i != size - 1) {builder.append(this.get(i) + ",");}else {builder.append(this.get(i));}}builder.append("]");return builder.toString();}public Iterator iterator() {return new Itr();}private class Itr<T> implements Iterator<T> {// 指向当前元素,默认第一个(索引是0)int cursor = 0;// 指定当前元素的前一个元素用途在于remove使用int lastRet = -1;@Overridepublic boolean hasNext() {return cursor != size;}@Overridepublic T next() {int i = cursor;cursor++;// 如果不进行hasNext()判断,就可能越界if(i > size) {throw new RuntimeException("没有这个元素了");}return (T)elementData[i];}}
}
1.4. 测试ArrayList
public class TestArrayList {public static void main(String[] args) {// 创建线性顺序表List list = new ArrayList();// 向末尾添加元素list.add("111");list.add("222");list.add(2, "AAAA");// 进行各种操作验证添加System.out.println(list.size()); // 3System.out.println(list.isEmpty()); // falseSystem.out.println(list.get(2)); // AAAASystem.out.println(list.contains("2")); // falseSystem.out.println(list.indexOf("222")); // 1System.out.println(list.toString()); // [111,222,AAAA]Iterator<String> it = list.iterator();while (it.hasNext()) {String elem = it.next();System.out.println(elem);}}
}
2. 手写单链表和LinkedList
2.1. 单链表技能点
- 认识单链表
* 特点
- 数据元素的存储对应的是不连续的存储空间,每个存储节点对应一个需要存储的数据元素
- 每个节点是由数据域和指针域组成。元素之间的逻辑关系通过存储节点之间的链接关系反映出来。逻辑上相邻的节点物理上不必相邻。
- 缺点
- 比顺序存储结构的存储密度小(每个节点都由数据域和指针域组成,所以相同空间内假设全存满,顺序比链式存储的更多)
- 按照索引查找节点时,链式存储比顺序存储满(每个节点地址不连续、无规律,导致按照索引查询效率低下)
- 优点
- 插入、删除灵活(不必移动节点,只要改变节点中的指针,但是需要先定位到元素上)
- 有元素才会分配节点空间,不会有闲置的节点
- 添加节点
- 删除节点
- 带头节点的单链表
在使用单链表实现线性表时,为了使程序更加简洁,通常在单链表最前面添加一个哑元节点,也称头节点。
在头节点中不存储任何实质的数据对象,其next域指向线性表中0号元素所在的节点
可以对空表、非空表的情况以及对首元节点进行统一管理,编程更方便,常用头节点。
一个不带头节点和带头节点的单链表实现线性表的结构如图所示
2.2. 手写单链表
- 定义单链表节点
public class Node {// 存储数据Object data;// 指向下一个节点的指针Node next;public Node() {super();}public Node(Object data) {super();this.data = data;}public Node(Object data, Node next) {super();this.data = data;this.next = next;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public Node getNext() {return next;}public void setNext(Node next) {this.next = next;}@Overridepublic String toString() {return "Node{" +"data=" + data +", next=" + next +'}';}
}
- 手写单链表
public class SingleLinkedList implements List{// 头节点,哑巴节点,不存储数据,为了方便编程private Node head = new Node();// 节点的个数private int size = 0;@Overridepublic int size() {return size;}@Overridepublic Object get(int i) {// i从0开始// 判断i值是否合理if(i < 0 || i >= size) {throw new IndexOutOfBoundsException("索引越界:" + i);}// 从第一个节点开始查找Node p = head.next; // p初始指向第一个节点(不是头节点)for (int j = 0; j < i; j++) {// p指向下个节点p = p.next;}// 返回第i个节点,而不是第i个节点的datareturn p;}@Overridepublic void add(int i, Object e) {// i值合理性判断if(i < 0 || i > size) {throw new IndexOutOfBoundsException("索引越界:" + i);}// 先找到前一个(如果当前索引是0,前一个就是head)Node p = head;if(i != 0) {p = (Node)this.get(i - 1);}// 创建一个新节点,给data复制Node newNode = new Node(e);// 指定新节点的下一个节点newNode.next = p.next;// 指定前一个节点的下一个节点是当前新节点p.next = newNode;// 数量+1size++;}@Overridepublic Object remove(int i) {if(i < 0 || i>= size) {throw new IndexOutOfBoundsException("索引越界:" + i);}// 先找到前一个(如果当前索引是0,前一个就是head)Node p = head;if(i != 0) {p = (Node)this.get(i - 1);}// 指向要删除的节点Node currentNode = p.next;// 完成删除操作p.next = currentNode.next;currentNode.next = null;// 提示将要删除的节点变成垃圾currentNode = null;// 数量-1size--;return null;}@Overridepublic String toString() {StringBuilder builder = new StringBuilder("[");// p初始指向第一个节点(不是头节点)Node p = head.next;for (int j = 0; j < size; j++) {builder.append(p.data + " ");// p指向下一个节点p = p.next;}builder.append("]");return builder.toString();}@Overridepublic boolean isEmpty() {return size == 0;}
}
-
单链表添加、删除、查询原理
-
测试单链表
public class TestList {public static void main(String[] args) {java.util.ArrayList list2;// 创建线性顺序表List list = new SingleLinkedList();// 向末尾添加元素list.add("1");list.add("2");list.add("3");list.add(3, "4");list.remove(2);// 进行各种操作验证添加System.out.println(list.size()); // 3System.out.println(list.isEmpty()); // falseSystem.out.println(list.contains("4")); // trueSystem.out.println(list.indexOf("2")); // 1System.out.println(list.toString()); // [1 2 4]}
}
2.3. 手写LinkedList
- LinkedList底层原理
- LinkedList底层是一个双向链表;添加、删除元素效率高;按照索引查找效率低。可以两个方向查询
- 每个节点都包含了对前一个和后一个元素的引用
- LinkedList同时实现了List、Deque、Queue接口,所以可以当作线性表、队列、双端队列、栈使用
- LinkedList是非同步的(线程不安全)
- 不存在LinkedList容量不足的问题
- LinkedList底层是一个双向链表;添加、删除元素效率高;按照索引查找效率低。可以两个方向查询
- 定义LinkedList节点
public class LinkedList implements List{/** 静态内部类,代表LinkedList的每个节点* */public static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}@Overridepublic String toString() {return "Node{" +"item=" + item +", next=" + next +", prev=" + prev +'}';}}
}
public class LinkedList implements List{transient int size = 0;transient Node first;transient Node last;@Overridepublic int size() {return size;}@Overridepublic Object get(int i) {return node(i).item;}@Overridepublic boolean isEmpty() {return size == 0;}@Overridepublic boolean contains(Object e) {return false;}@Overridepublic int indexOf(Object e) {return 0;}@Overridepublic void add(int index, Object element) {if(index == size) {linkLast(element);}else {linkBefore(element, node(index));}}Node node(int index) {if(index < (size >> 1)) {Node x = first;for (int i = 0; i < index; i++) {x = x.next;}return x;}else {Node x = last;for (int i = size - 1; i > index; i--) {x = x.prev;}return x;}}void linkBefore(Object e, Node succ) {final Node pred = succ.prev;final Node newNode = new Node<>(pred, e, succ);succ.prev = newNode;if(pred == null) {first = newNode;}else {pred.next = newNode;}size++;}@Overridepublic void add(Object e) {linkLast(e);}public void linkLast(Object e) {final Node l = last;final Node newNode = new Node<>(l, e, null);last = newNode;if(l == null) {first = newNode;}else {l.next = newNode;}size++;}public String toString() {return first.toString();}
}
- LinkedList的add(Object)方法的实现过程
- 测试LinkedList
public class TestList1 {public static void main(String[] args) {// 创建线性顺序表List list = new LinkedList();// 向末尾添加元素list.add(1);list.add(2);list.add(3);list.add(3, 4);// 进行各种操作验证添加System.out.println(list.size()); // 4System.out.println(list.isEmpty()); // falseSystem.out.println(list.get(0)); // 1}
}
3. 手写HashMap
3.1. HashMap的原理
- 底层结构是哈希表,采用了顺序表+链表结合的结构;同一个链表上所有元素的存储地址都是相同的,是发生冲突的元素
- 链表上每个节点的就是一个Entry,字段包括四部分
同一个链表上节点的哈希码可能不相同,存储的地址是相同的 - 哈希表的优点
- 添加块、查询块(通过计算得到存储位置,不是通过比较)
- 无序
- key唯一
- 关键参数
- 默认主数组长度16
- 默认装填因子0.75
- 每次主数组扩容为原来的2倍
- JDK8中,当链表长度大于8时,链表变为红黑树
- 第一步计算哈希码,不仅调用了hashCode(),又进行了多次散列。目的在于key不同,哈希码尽量不同,减少冲突
final int hash(Object k) {int h = 0;h ^= k.hashCode();h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);
}
- 细节:发生冲突,经过比较不存在相同key的元素,要添加一个新的节点。不是加到链表最后,而是添加到链表最前。
3.2. 定义Map接口
public interface Map {// 定义方法public void put(Object key, Object value);public Object get(Object key);public int size();public boolean isEmpty();// 定义内部接口interface Entry {public Object getKey();public Object getValue();}
}
3.3. 手写HashMap
public class HashMap implements Map{// 指向哈希表的数组引用public Entry[] table;// 键值对数量int size;public HashMap() {// 哈希表主数组默认长度16table = new Entry[11];}@Overridepublic void put(Object key, Object value) {// 计算哈希码int hash = key.hashCode();// 根据哈希码计算存储位置int index = hash % table.length;// 存入指定位置// 如果该位置没有元素,增加一个节点if(table[index] == null) {table[index] = new Entry(hash, key, value);size++;}else {// 如果有元素,进行逐个比较Entry entry = table[size];while (entry != null) {// 如果找到了相同的keyif(entry.getKey().equals(key)) {// 新的value替代旧的valueentry.value = value;return;}entry = entry.next;}// 循环结束,表明也没有相同的key,在链表最前添加一个节点Entry firstEntry = table[index];table[index] = new Entry(hash, key, value, firstEntry);size++;}}@Overridepublic Object get(Object key) {// 计算哈希码int hash = key.hashCode();// 根据哈希码计算存储位置int index = hash % table.length;// 查找对应的EntryEntry entry = null;if(table[index] != null) {Entry currentEntry = table[index];while (currentEntry != null) {if(currentEntry.getKey().equals(key)) {entry = currentEntry;break;}currentEntry = currentEntry.next;}}return entry == null ? null : entry.getValue();}@Overridepublic String toString() {StringBuilder builder = new StringBuilder("{");for (int i = 0; i < table.length; i++) {if(table[i] != null) {Entry entry = table[i];while (entry != null) {builder.append(entry.getKey() + "=" + entry.getValue() + "," + entry = entry.next);}}}if(builder.length() != 1) {builder.deleteCharAt(builder.length() - 1);}builder.append("}");return builder.toString();}@Overridepublic int size() {return size;}@Overridepublic boolean isEmpty() {return size == 0;}
}
public class Entry implements Map.Entry{private int hash;private Object key;private Object value;// 指向下一个节点的指针private Entry next;public Entry() {}public Entry(int hash, Object key, Object value) {this.hash = hash;this.key = key;this.value = value;}public Entry(int hash, Object key, Object value, Entry next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}@Overridepublic Object getKey() {return key;}@Overridepublic Object getValue() {return value;}@Overridepublic String toString() {return "Entry{" +"hash=" + hash +", key=" + key +", value=" + value +", next=" + next +'}';}
}
3.4 测试HashMap
public class TestHashMap {public static void main(String[] args) {java.util.HashMap map1;HashMap map = new HashMap();map.put(23, "china");map.put(36, "Japan");System.out.println(map.size());System.out.println(Arrays.toString(map.table));System.out.println(map);}
}
4. 新一代并发集合类
4.1. 手写HashSet
- HashSet底层就是HashMap,哈希表的每个Entry的key是每个元素,value统一都是new Object()
public interface Set {public int size();public void add(Object obj);public boolean isEmpty();public boolean contains(Object obj);
}
public class HashSet implements Set {private transient HashMap map;private static final Object PRESENT = new Object();public HashSet() {this.map = new HashMap();}@Overridepublic int size() {return map.size();}@Overridepublic void add(Object key) {map.put(key, PRESENT);}@Overridepublic boolean isEmpty() {return map.isEmpty();}@Overridepublic boolean contains(Object obj) {return map.get(obj) != null;}
}
4.2. 三代集合类发展过程
- 第一代线程安全集合类
- Vector、Hashtable:使用synchronized修饰方法来保证线程安全
- 缺点:效率低下
- 第二代线程非安全集合类(主流)
- ArrayList、HashMap:线程不安全,但性能好,用来替代Vector、Hashtable
- 线程安全的:使用Collections.synchronizedList(list)和Collections.synchronizedMap(m),底层使用synchronized代码块锁,虽然也是锁住了所有的代码,但是所在方法里面,比所在方法外性能稍有提高,毕竟进方法本身是要分配资源的
- 第三代线程安全集合类(波浪式前进,螺旋式上升)
- 在有大量并发下,使用java.util.concurrent.*中的ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet来提高集合的效率和安全,底层大都采用Lock锁(1.8的ConcurrentHashMap不使用Lock锁),保证安全的同时,性能也很高
4.3. 新一代并发集合类
- ConcurrentHashMap:分段(segment)锁定+Lock锁
- JDK1.7中
- 比HashMap的线程安全,并且性能比Hashtable好,Collections.synchronizedMap(m)都有提高
- 使用的不是synchronized代码块锁或方法锁,使用了锁分离技术,用多个锁来控制对hash表的不同部分(段segment)进行的修改,采用ReentrantLock锁来实现
- 如果多个修改操作发生在不同的段上,就可以并发执行,从而提高效率
- JDK1.8中
- 摒弃了segment锁段的概念,而是采用了volatile+CAS实现无锁化操作。**底层由数组+链表+红黑树的方式(JDK1.8中HashMap的实现)。**为了做到并发,又增加了很多辅助的类,如TreeBin,Traverser等对象内部类
- JDK1.7中
- CopyOnWriteArrayList:CopyOnWrite+Lock锁
- 对于set()、add()、remove()等方法使用ReentrantLock的Lock和unlock来加锁和解锁,读操作不需要加锁(之前集合安全类,即使读操作也要加锁,保证数据的实时一致)
- CopyOnWrite原理:写时复制
- 当往一个容器中添加元素时,不直接添加,而是先将容器进行copy,然后往新容器中添加元素
- 添加完元素之后,再将原容器的引用指向新容器。这样做的好处是可以对CopyOnWrite容器进行并发读,而不需要加锁,因为当前容器不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写使用不同的容器。
- 适合对于读操作远多于写操作的应用,特别在并发的情况下,可以提高性能的并发读取
- CopyOnWrite容器只能保证数据的最终一致性,不能保证数据实时一致性。因此,如果希望写入数据马上能读到,就不要使用CopyOnWrite容器
public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;// 复制出新数组Object[] newElements = Arrays.copyOf(elements, len + 1);// 把新元素添加到新数组中newElements[len] = e;// 把原数组引用指向新数组setArray(newElements);return true;}finally {lock.unlock();}}final void setArray(Object[] a) {array = a;}public E get(int index) {return get(getArray(), index);}
注意:读时不需要加锁,如果读时有多个线程正在向ArrayList添加数据,读还是会读到旧数据,因为写时不会锁住旧的ArrayList
- CopyOnWriteArraySet:CopyOnWrite+Lock锁
- 线程安全的无序集合,CopyOnWriteArraySet和HashSet都继承于共同的父类AbstractSet;但是HashSet是通过散列表HashMap实现的,而CopyOnWriteArraySet是通过动态数组CopyOnWriteArrayList实现的
- CopyOnWriteArraySet在CopyOnWriteArrayList基础上使用了Java的装饰模式,所以底层是相同的。 而CopyOnWriteArrayList本质是动态数组队列,所以CopyOnWriteArraySet相当于通过动态数组实现的集合
- CopyOnWriteArrayList中允许有重复的元素;但CopyOnWriteArraySet不能有重复的元素。因此,CopyOnWriteArrayList额外提供了addIfAbsent()和addAllAbsent()这两个添加元素的API,通过API来添加元素,只有当元素不存在时才执行添加操作。
相关文章:

Java集合提升
1. 手写ArrayList 1.1. ArrayList底层原理细节 底层结构是一个长度可以动态增长的数组(顺序表)transient Object[] elementData; 特点:在内存中分配连续的空间,只存储数据,不存储地址信息。位置就隐含着地址。优点 节…...

uniapp 微信小程序生成水印图片
效果 源码 <template><view style"overflow: hidden;"><camera device-position"back" flash"auto" class"camera"><cover-view class"text-white padding water-mark"><cover-view class"…...

ElasticSearch相关知识点
ElasticSearch中的倒排索引是如何工作的? 倒排索引是ElasticSearch中用于全文检索的一种数据结构,与正排索引不同的是,正排索引将文档按照词汇顺序组织。而倒排索引是将词汇映射到包含该词汇的文档中。 在ElasticSearch中,倒排索…...

css 文字图片居中及网格布局
以下内容纯自已个人理解,直接上代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...

解决ImportError: DLL load failed while importing _rust: 找不到指定的程序
解决ImportError: DLL load failed while importing _rust: 找不到指定的程序 python使用库cryptography 当 from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions 时,会报错: ImportError: DLL load failed while importin…...

集合-List去重
1.利用Set去重 @Test public void distinctList() {List<String> oldList = new ArrayList<>();oldList.add("a");oldList.add("a");oldList.add("b");oldList.add("c");oldList.add("d");List<String> …...

ST-LINK USB communication error 非常有效的解决方法
文章目录 一、检查确定是ST-LINK USB communication error的问题二、关闭文件,打开keil软件所在文件夹,找到STLink文件夹,找到该应用程序双击 一、检查确定是ST-LINK USB communication error的问题 二、关闭文件,打开keil软件所在…...

探索CSS的:future-link伪类:选择指向未来文档的链接
CSS(层叠样式表)是Web设计中用于描述网页元素样式的语言。随着CSS4的提案,引入了许多新的选择器,其中之一是:future-link伪类。然而,需要注意的是,:future-link伪类目前还处于提议阶段,并没有在…...

【C++】序列与关联容器(三)map与multimap容器
【C】序列与关联容器(三)map与multimap容器 一、map二、multiset / multimap 一、map 树中的每个结点的类型是一个std::pair //pair的类型是<const key,value> pair是一个包含两个指针的结构体,第一个指针指向该节点的key,…...

ActiveMQ、RabbitMQ、Kafka、RocketMQ在优先级队列、延迟队列、死信队列、重试队列、消费模式、广播模式的区别
ActiveMQ、RabbitMQ、Kafka、RocketMQ这四款消息队列在优先级队列、延迟队列、死信队列、重试队列、消费模式、广播模式等方面各有其特点和差异。以下是对这些方面的详细比较: 1. 优先级队列 ActiveMQ:支持优先级队列,可以在发送消息时指定…...

首款会员制区块链 Geist 介绍
今天,Pixelcraft Studios 很高兴地宣布即将推出 Geist,这是一个由 Base、Arbitrum、Alchemy 以及 Aavegotchi 支持的全新 L3。 Geist 之前的代号为 “Gotchichain”,是首个专为游戏打造的会员专用区块链。 为什么选择 Geist? …...

CANoe软件中Trace窗口的筛选栏标题不显示(空白)的解决方法
文章目录 问题描述原因分析解决方案扩展知识总结问题描述 不知道什么情况,CANoe软件中Trace窗口的筛选栏标题突然不显示了,一片空白。现象如下: 虽然不影响CANoe软件的使用,但是观感上非常难受,对于强迫症患者非常不友好。 原因分析 按照常规思路,尝试了: 1、重启CAN…...

日期类代码实现-C++
一、目标 通过前面对类和对象的介绍我们可以自己通过C代码初步实现一个简单的日期类。 实现的主要操作有: 1.日期类的构造函数 2.日期类的拷贝构造函数(在头文件中实现) 3.日期类的比较运算符重载 4.日期类的计算运算符重载 5.流插入运…...

【问题记录+总结】VS Code Tex Live 2024 Latex Workshop Springer模板----更新ing
目录 Summary 道阻且长 少即是多 兵马未动粮草先行 没有万能 和一劳永逸 具体问题具体分析 心态 Detail 1、关于模板[官网] 2、settings.json 3、虫和杀虫剂 4、擦 换成Tex Studio都好了。。。 Summary 道阻且长 某中意期刊,只有Latex。之前只简单用过…...

Linux运维_Bash脚本_源码安装Go-1.21.11
Linux运维_Bash脚本_源码安装Go-1.21.11 Bash (Bourne Again Shell) 是一个解释器,负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件,并于 1989 年发布的免费软件,作为 Sh (Bourne Shell) 的替代品。 您可以在 Linux 和…...

ShareSDK Twitter
创建应用 1.登录Twitter控制台并通过认证 2.点击Developer Portal进入Twitter后台 3.点击Sign up for Free Account创建应用 4.配置应用信息 以下为创建过程示例,图中信息仅为示例,创建时请按照真实信息填写,否则无法正常使用。 权限申请…...

word2vec 如何用多个词表示一个句子
word2vec 模型通常用于将单词映射为固定大小的向量。为了使用多个词表示一个句子,我们可以采用以下几种方法: 词袋模型 (Bag of Words, BoW): 将句子中所有词的向量加起来,不考虑词的顺序。这种方法简单,但会丢失词序信息。 计算…...

IDEA中查看接口的所有实现类和具体实现类
1.IDEA中接口的所有实现类查看 1.CTRLH(hierarchy 结构) 我们选中要查看的接口 按住快捷键ctrlh 在界面右侧可以看到该接口的所有可能实现类 2.右击diagrams->show diagram 选中要查看的接口 右击选择diagrams->show diagram 即可以以图表的方式查看接口和所有实现类…...

DLL的导出和调用
动态链接库在C中非常重要,写了一个简单的例子用于DLL的导出和调用。 DLL的生成 头文件 #include<iostream> #include<stdexcept> using namespace std;#define TESTAPI __declspec(dllexport)// 函数定义 extern "C" {TESTAPI int add(in…...

vscode中调试cuda kernel
关于vscode中调试cpp可参考之前的博客:ubuntu vscode 基本设置 和 调试设置_ubuntu vscode 调试-CSDN博客 这篇我们来讲如何调试.cu的kernel,主要参考的是:https://www.zhihu.com/question/431782036/answer/2468791220 1、基本准备不多说&am…...

SQL的连接查询与pandas的对应关系
在SQL和Pandas中,连接查询(join)是处理数据集之间关系的重要工具。下面是SQL中的各种连接查询类型及其与Pandas中相应操作的对应关系: 1. INNER JOIN SQL: INNER JOIN 返回两个表中具有匹配值的行。 Pandas: merge() 方法的 how…...

【JS】中断和恢复任务序列
前言 封装processTasks函数,实现以下需求 /*** 依次顺序执行一系列任务* 所有任务全部完成后可以得到每个任务的执行结果* 需要返回两个方法,start用于启动任务,pause用于暂停任务* 每个任务具有原子性,即不可中断,只…...

CentOS系统下安装NVIDIA显卡驱动
一、安装显卡驱动 1.安装依赖项 yum -y install gcc pciutils yum -y install gcc yum -y install gcc-c yum -y install make2.查看内核版本 uname -a3.查看显卡版本 lspci | grep -i nvidia4.屏蔽系统自带的nouveau (1)查看nouveau lsmod | grep nouveau (2)打开blackl…...

Linux 与 Windows 服务器操作系统 | 全面对比
在服务器操作系统的领域,Linux 和 Windows 一直是两个备受关注的选择。 首先来看 Windows 操作系统。它由 Microsoft Corporation 开发,在桌面领域占据显著份额,其中 Windows 10 是使用最广泛的版本,广泛应用于个人计算机和企业桌…...

给既有exe程序添加一机一码验证
原文地址:李浩的博客 lihaohello.top 本科期间开发过一款混凝土基本构件设计程序,该程序是一个独立的exe可执行文件,采用VC静态链接MFC库编制而成。近期,需要为该程序添加用户注册验证的功能,从而避免任何用户获取该程…...

【Datawhale X 魔搭 】AI夏令营第四期大模型方向,Task2:头脑风暴会,巧灵脑筋急转弯(持续更新)
队伍名称:巧灵脑筋急转弯 队伍技术栈:python,LLM,RAG,大模型,nlp,Gradio,Vue,java 队友:知唐(队长),我真的敲不动…...

mysql 多个外键
在MySQL中,一个表可以有多个外键约束,它们分别关联到不同的主表。在创建表时,可以在每个外键约束上指定不同的外键名称。以下是一个简单的例子,演示如何在创建表时定义多个外键: CREATE TABLE orders (order_id INT AU…...

解决方案上新了丨趋动科技推出基于银河麒麟操作系统的异构算力池化解决方案
趋动科技携手麒麟软件打造基于银河麒麟操作系统的异构算力池化解决方案,共同探索AI领域新场景。 人工智能技术作为数字经济发展的重要推手,在各行业业务场景中落地需要大量AI算力资源的有效保障。在IT基础设施普遍云化的今天,AI算力一方面需…...

14.创建一个实战maven的springboot项目
项目核心主要部分 pom.xml文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://mave…...

docker部署LNMP
docker部署LNMP nginx 1.22 172.111.0.10 docker-nginx mysql 8.0.30 172.111.0.20 docker-mysql php 8.1.27 172.111.0.30 docker-php docker:单节点部署,只能在一台机器上部署,如果跨机器容器无法操作,无法通信。 做高可用…...