常用数据结构之String字符串

字符串

在Java编程语言中,字符可以使用基本数据类型char来保存,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

操作字符串常用的有三种类:String、StringBuilder、StringBuffer

接下来看看这三类常见用法

String

String value = new String("测试下");
System.out.println(value);

特点:

不可变

String value_a = "测试下原来值";
        value_a = "改变了";
        System.out.println(value_a);

实际打印

改变了

看起来好像改变了,那string为啥还具有不可变的特点呢

那咱来看看以上程序执行过程中value_a的指向

可以看到原来value_a指向“测试下原来值”字符串,后来有指向了“改变了”

为啥String不可变,咱进入String源码看看咋实现的

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    /**
     * Class String is special cased within the Serialization Stream Protocol.
     *
     * A String instance is written into an ObjectOutputStream according to
     * <a href="{@docRoot}/../platform/serialization/spec/output.html">
     * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

    /**
     * Initializes a newly created {@code String} object so that it represents
     * an empty character sequence.  Note that use of this constructor is
     * unnecessary since Strings are immutable.
     */
    public String() {
        this.value = "".value;
    }

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    /**
     * Allocates a new {@code String} so that it represents the sequence of
     * characters currently contained in the character array argument. The
     * contents of the character array are copied; subsequent modification of
     * the character array does not affect the newly created string.
     *
     * @param  value
     *         The initial value of the string
     */
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

    /**
     * Allocates a new {@code String} that contains characters from a subarray
     * of the character array argument. The {@code offset} argument is the
     * index of the first character of the subarray and the {@code count}
     * argument specifies the length of the subarray. The contents of the
     * subarray are copied; subsequent modification of the character array does
     * not affect the newly created string.
     *
     * @param  value
     *         Array that is the source of characters
     *
     * @param  offset
     *         The initial offset
     *
     * @param  count
     *         The length
     *
     * @throws  IndexOutOfBoundsException
     *          If the {@code offset} and {@code count} arguments index
     *          characters outside the bounds of the {@code value} array
     */
    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

    /**
     * Allocates a new {@code String} that contains characters from a subarray
     * of the <a href="Character.html#unicode">Unicode code point</a> array
     * argument.  The {@code offset} argument is the index of the first code
     * point of the subarray and the {@code count} argument specifies the
     * length of the subarray.  The contents of the subarray are converted to
     * {@code char}s; subsequent modification of the {@code int} array does not
     * affect the newly created string.
     *
     * @param  codePoints
     *         Array that is the source of Unicode code points
     *
     * @param  offset
     *         The initial offset
     *
     * @param  count
     *         The length
     *
     * @throws  IllegalArgumentException
     *          If any invalid Unicode code point is found in {@code
     *          codePoints}
     *
     * @throws  IndexOutOfBoundsException
     *          If the {@code offset} and {@code count} arguments index
     *          characters outside the bounds of the {@code codePoints} array
     *
     * @since  1.5
     */
    public String(int[] codePoints, int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count;

        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }
}

可以看到String类有个final修饰符,final修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的

相当于string类型指向的具体内容不会变,例如刚才的例子,

value_a = "测试下原来值"; 这个数值不会变,原本有这个字符串有char value[]来进行保存本质不会被修改
value_a = "改变了"; 后续改变value_a的数值,会把原本value_a的引用指向新的字符串"改变了",而不是改变原来有char value[]保存的"测试下原来值",但由于无引用指向原来的字符串"测试下原来值",会被垃圾回收掉

结论:String不可变指的是String的内容不会变,但是可以改变String引用指向新的字符串

接下来看个🌰

String pre = "Hello,World";
        String new_value = "Hello,World";
        String object_value = new String("Hello,World");
        System.out.println(object_value.hashCode());
        System.out.println(pre.hashCode());
        System.out.println(new_value.hashCode());
        System.out.print("通过==判断pre是否与new_value相等->");
        System.out.println(pre==new_value);
        System.out.print("通过==判断pre是否与object_value相等->");
        System.out.println(pre==object_value);
        System.out.println("----------------");
        System.out.print("通过.equals判断pre是否与new_value相等->");
        System.out.println(pre.equals(new_value));
        System.out.print("通过.equals判断pre是否与object_value相等->");
        System.out.println(pre.equals(object_value));

以上程序分别会打印true还是false呢

可以看到通过字面创建的"Hello,World",不管新建多少次,==判断和equals判断都相等

通过对象创建的"Hello,World"和字面量新建的字符串通过==判断也相等,但是通过equals判断对象创建和字面量创建返回的false

首先来看看equals和==的区别

equals与==区别

==:判断两个对象的地址是否相等

equals:Object超级父类有个equals方法

    public boolean equals(Object obj) {
        return (this == obj);
    }

Object是直接判断两者地址是否相同,与==作用相同

而且所有的对象都会继承Object类

可以看看官方的解释

package java.lang;

/**
 * Class {@code Object} is the root of the class hierarchy.
 * Every class has {@code Object} as a superclass. All objects,
 * including arrays, implement the methods of this class.
 *类对象是类层次结构的根。每个类都有Object作为超类。所有对象,包括数组,都实现了此类的方法。
 * @author  unascribed
 * @see     java.lang.Class
 * @since   JDK1.0
 */
public class Object

所以String也继承了Object类,具有equals方法

来看看String的equals的方法咋实现的呢

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;//假设两个对象地址相同==,则返回true 相当于两者相同
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {//循环遍历String的value数组
                    if (v1[i] != v2[i])
                        return false;//其中有任何一个不同,认定两者不同
                    i++;
                }
                return true; //所有字符都相同的情况下,相当于两者相同
            }
        }
        return false; //把不是String类与String对比 相当于两者不同
    }

可以看出来String的equals方法

1,假设两个对象地址相同==,则返回true 相当于两者相同

2,判断对象是否属于string

3,循环遍历String的value数组,其中有任何一个不同,认定两者不同

4,所有字符都相同的情况下,相当于两者相同

5,不是String类与String对比 相当于两者不同

而Object是直接判断两者地址是否相同

所以再看看之前举的🌰

String pre = "Hello,World";
        String new_value = "Hello,World";
        String object_value = new String("Hello,World");
        System.out.println(object_value.hashCode());
        System.out.println(pre.hashCode());
        System.out.println(new_value.hashCode());
        System.out.print("通过==判断pre是否与new_value相等->");
        System.out.println(pre==new_value);
        System.out.print("通过==判断pre是否与object_value相等->");
        System.out.println(pre==object_value);
        System.out.println("----------------");
        System.out.print("通过.equals判断pre是否与new_value相等->");
        System.out.println(pre.equals(new_value));
        System.out.print("通过.equals判断pre是否与object_value相等->");
        System.out.println(pre.equals(object_value));

pre和new_value都是通过字面量的形式创建的字符串,"Hello,World"字符串会保存到常量池当中,新建了pre引用变量和new_value引用变量指向的同一个"Hello,World"对象,所以两者本质属于同个对象,所以==和equals都相同

object_value在堆中新建了对象,object_value引用变量指向堆中的"Hello,World"对象

所以pre==object_value会返回false,两者属于两个对象

pre.equals(new_value)返回true,由于String对equals重写了,只需两者都是String对象,且value数组的值都相同则返回true

hashcode

再看看为啥这三者的hashcode都相同呢

直接看源码

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

获取哈希码,也称为散列码

hashCode()方法‌:用于返回对象的哈希码,这是一个整数值。哈希码用于确定对象在哈希表中的索引位置。理想情况下,不同的对象应该产生不同的哈希码,但这不是强制的。哈希码的计算通常基于对象的属性,不同的对象可能产生相同的哈希码,这就是所谓的哈希冲突。

上述hashcode实现简化成:

value[n],字符数组有n个字符 计算规则顺序 定义h[n]为当前遍历计算出来的值

n=0   h[0]=value[0]

n=1:  h[1]=h[0]*31+value[1]

n=2: h[2] = h[1]*31+value[2]=(value[0]*31+value1)*31+value[2]

选择 31原因:

优化计算‌:31可以被编译器优化为31 * i = (i << 5) - i,这种形式利用了位运算和减法,比直接的乘法运算效率更高‌

减少冲突‌:31是一个质数,使用质数作为乘法因子可以降低哈希冲突的概率。质数的乘法性质使得每个字符的哈希值与31相乘时,都能保持相对独立的哈希值,从而减少冲突的可能性‌

31用2进制来表达为 11111 ,31*i等价于(i<<5)-i,即先将i左移5位(相当于乘以32),然后再减去i

注意点:

1,两者equals相同,则两者的hashcode返回整数值相同

2,若两者hashcode相同,equals不一定相同

3,不同的对象hashcode,为了使得减少哈希表的冲突,尽量保持不同

可以看到pre、new_value、object_value三者的value数组保存的数值相同都是"Hello,World"

所以通过hash算法算出来的索引为止即hash值相同

接下来再看个🌰

String a = "Hello";
        String b = "Hello";
        String c = new String("Hello");
        String e = a+b;
        String t = a+b+c;
        String l = a+"World";
        String l1 = "Hello"+World"

以上程序共新建了多少对象呢

a="Hello",一个对象

b由于"Hello"在字符串常量池中存在,

c 在堆中新建1个新的字符串常量对象"Hello",栈内存储c变量引用指向它

e 通过+来拼接字符串实际通过StringBuilder来构建的所有会新建个对象StringBuilder,

t a="Hello"在常量池中有,无需新建,b="Hello"在常量池中有,无需新建,c为堆对象引用,已新建过了,但是新的字符串"HelloHelloHello"会放入堆中新建个对象

l "World"和"Hello"会在编译期间拼接,新建"HelloWorld"对象,注意这里不会新建"World"对象进入常量池

l1 编译期间拼接成"HelloWorld" 新建1个对象 注意这里与l不同 l通过StringBuilder拼接的 两个对象部署同个对象

所以总共新建了6个对象

总结

字面量新建

String a = "Hello"; // 在字符串常量池中存入"Hello" 新建1个对象 a引用变量指向它
String b = "Hello";// 在字符串常量找到了"Hello" 无需新建对象 b引用变量指向它
String c = "World";// 在字符串常量池中找不到"World" 常量池中存入"World" 新建1个对象 c引用变量指向它

对象新建

String a = "Hello"; // 在字符串常量池中存入"Hello" 新建1个对象 a引用变量指向它
String b = "Hello";// 在字符串常量找到了"Hello" 无需新建对象 b引用变量指向它
String c = "World";// 在字符串常量池中找不到"World" 常量池中存入"World" 新建1个对象 c引用变量指向它
String t = new String("Hello");//"Hello"在字符串常量池中存在,无需在常量池在存入"Hello",在堆中新建对象指向它
String t1 = new String("HelloWorld");//"HelloWorld"在字符串常量池中不存在,在常量池在存入"HelloWorld",内存堆中新建对象指向它

+拼接

String a = "Hello"; // 在字符串常量池中存入"Hello" 新建1个对象 a引用变量指向它
String b = "Hello";// 在字符串常量找到了"Hello" 无需新建对象 b引用变量指向它
String c = "World";// 在字符串常量池中找不到"World" 常量池中存入"World" 新建1个对象 c引用变量指向它
String t = new String("Hello");//"Hello"在字符串常量池中存在,无需在常量池在存入"Hello",在堆中新建对象指向它
String t1 = new String("HelloWorld");//"HelloWorld"在字符串常量池中不存在,在常量池在存入"HelloWorld",内存堆中新建对象指向它
String b1 = "Hello"+"World";//"HelloWorld"在常量池中已有,所以无需新建对象
String new_value = "Hello"+"Java";//"HelloJava"在常量池中不存在,常量池新建"HelloJava"
String value_new = a+b;//通过StringBuilder拼接会新建个对象

拼接"Hello"常量:直接拼接,会在编译期间进入常量池

但不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以

  • 基本数据类型( bytebooleanshortcharintfloatlongdouble)以及字符串常量。
  • final 修饰的基本数据类型和字符串变量
  • 字符串通过 “+”拼接得到的字符串、基本数据类型之间算数运算(加减乘除)、基本数据类型的位运算(<<、>>、>>> )

引用的值在程序编译期是无法确定的,编译器无法对其进行优化

拼接常量引用变量:字符对象的拼接实际上底层是使用的StringBuilder的append方法,先将字符串对象转换成StringBuilder然后调用append方法之后再调用toString(),此时生成的是另一个String对象,String对象存储在堆中,不会存入常量池

注意:‌String().intern()方法在Java中的作用是将字符串对象添加到字符串常量池中,如果常量池中已经存在相同的字符串,则返回该字符串的引用;如果不存在,则创建一个新的字符串对象并添加到常量池中‌‌

对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

注意被final修饰的常量的引用拼接可以直接在编译期间再进入常量池

但修饰1个当时无法确定的数值,即在运行时才可以确定的值,则海还是会通过StringBuilder来拼接

开发中应该减少多个字符串拼接操作

所以出现了StringBuilder和StringBuffer

StringBuilder

由于string为不可变的,后续又设计了stringbuilder类

‌StringBuilder是Java中的一个可变字符串操作类,主要用于在需要频繁修改字符串内容的场景下使用,以提高性能。

StringBuilder的优势和适用场景

  1. 性能优势‌:StringBuilder是可变的,对字符串的修改直接作用于当前对象,无需创建新对象,因此在需要频繁拼接或修改字符串时,性能更高‌1。
  2. 适用场景‌:适合在单线程环境下使用,特别是在本地应用程序或单线程任务中需要频繁修改字符串时,StringBuilder的性能优于StringBuilder

源码

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the {@code capacity} argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }

    /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

    /**
     * Constructs a string builder that contains the same characters
     * as the specified {@code CharSequence}. The initial capacity of
     * the string builder is {@code 16} plus the length of the
     * {@code CharSequence} argument.
     *
     * @param      seq   the sequence to copy.
     */
    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

    /**
     * Appends the specified {@code StringBuffer} to this sequence.
     * <p>
     * The characters of the {@code StringBuffer} argument are appended,
     * in order, to this sequence, increasing the
     * length of this sequence by the length of the argument.
     * If {@code sb} is {@code null}, then the four characters
     * {@code "null"} are appended to this sequence.
     * <p>
     * Let <i>n</i> be the length of this character sequence just prior to
     * execution of the {@code append} method. Then the character at index
     * <i>k</i> in the new character sequence is equal to the character at
     * index <i>k</i> in the old character sequence, if <i>k</i> is less than
     * <i>n</i>; otherwise, it is equal to the character at index <i>k-n</i>
     * in the argument {@code sb}.
     *
     * @param   sb   the {@code StringBuffer} to append.
     * @return  a reference to this object.
     */
    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }

    @Override
    public StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }
}

可以看出来构造函数有4个

StringBuilder():字符初始容量为16的StringBuilder

StringBuilder(int capacity):字符初始容量为指定数量的StringBuilder

StringBuilder(CharSequence seq):包含与指定的CharSequence相同的字符序列

StringBuilder(String str):包含与指定的String相同的字符序列

常用方法:

append():

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

可以看出来返回的对象是this,不会增加新的对象,对比String内存占用少了很多

insert():

public AbstractStringBuilder insert(int offset, char[] str) {
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        int len = str.length;
        ensureCapacityInternal(count + len);
        System.arraycopy(value, offset, value, offset + len, count - offset);
        System.arraycopy(str, 0, value, offset, len);
        count += len;
        return this;
    }

可以看出来返回的对象是this,不会增加新的对象,对比String内存占用少了很多

toString():

@Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

StringBuilder类不是线程安全的,有多个线程同时对同一个StringBuilder对象进行操作,可能会出现并发问题。

StringBuffer

线程安全的,主要原因在于其内部方法关键字进行同步‌。这意味着多个线程可以安全地同时访问和修改同一个StringBuffer对象,而不会导致数据不一致或其他线程相关的问题‌

@Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

synchronized即同步锁,当前线程执行情况下,其它线程会同步等待直至当前线程释放锁

    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ 消息顺序性保证

方式一&#xff1a;Consumer设置exclusive 注意条件 作用于basic.consume不支持quorum queue 当同时有A、B两个消费者调用basic.consume方法消费&#xff0c;并将exclusive设置为true时&#xff0c;第二个消费者会抛出异常&#xff1a; com.rabbitmq.client.AlreadyClosedEx…

使用LLaMA Factory踩坑记录

前置条件&#xff1a;电脑显卡RTX 4080 问题&#xff1a;LLaMA-Factory在运行的时候&#xff0c;弹出未检测到CUDA的报错信息 结论&#xff1a;出现了以上的报错&#xff0c;主要可以归结于以下两个方面&#xff1a; 1、没有安装GPU版本的pytorch&#xff0c;下载的是CPU版本…

大模型推理——MLA实现方案

1.整体流程 先上一张图来整体理解下MLA的计算过程 2.实现代码 import math import torch import torch.nn as nn# rms归一化 class RMSNorm(nn.Module):""""""def __init__(self, hidden_size, eps1e-6):super().__init__()self.weight nn.Pa…

Vue3+codemirror6实现公式(规则)编辑器

实现截图 实现/带实现功能 插入标签 插入公式 提示补全 公式验证 公式计算 需要的依赖 "codemirror/autocomplete": "^6.18.4","codemirror/lang-javascript": "^6.2.2","codemirror/state": "^6.5.2","cod…

MIT 6.5940(一)

记录了Lecture 1~8 Lecture 1 Introduction TinyML and Efficient Deep Learning Computing 摘要 AI systems need to continually adapt to new data collected locally 在设备学习&#xff1a;better privacy, lower cost, customization, life-long learningTraining is…

Linux TCP 编程详解与实例

一、引言 在网络编程的领域中&#xff0c;TCP&#xff08;Transmission Control Protocol&#xff09;协议因其可靠的数据传输特性而被广泛应用。在 Linux 环境下&#xff0c;使用 C 或 C 进行 TCP 编程可以实现各种强大的网络应用。本文将深入探讨 Linux TCP 编程的各个方面&…

一款由 .NET 官方团队开源的电子商务系统 - eShop

项目介绍 eShop是一款由.NET官方开源的&#xff0c;基于.NET Aspire构建的用于参考学习的服务架构电子商务系统&#xff0c;旨在展示如何利用.NET框架及其相关技术栈构建一个现代化的电子商务网站。该项目采用服务架构&#xff0c;将应用程序分解为多个独立的服务&#xff0c;…

crewai框架第三方API使用官方RAG工具(pdf,csv,json)

最近在研究调用官方的工具&#xff0c;但官方文档的说明是在是太少了&#xff0c;后来在一个视频里看到了如何配置&#xff0c;记录一下 以PDF RAG Search工具举例&#xff0c;官方文档对于自定义模型的说明如下&#xff1a; 默认情况下&#xff0c;该工具使用 OpenAI 进行嵌…

2011-2020年各省电话普及率数据

2011-2020年各省电话普及率数据 1、时间&#xff1a;2011-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、电话普及率(包括移动电话)(部/百人) 4、范围&#xff1a;31省 5、指标说明&#xff1a;电话普及率是衡量一个…

【自开发工具介绍】SQLSERVER的ImpDp和ExpDp工具演示05

SQLSERVER的ImpDp和ExpDp工具演示 1、表部分数据导出 (-query) ※「-query」和「-include_table」必须一起使用 「-query」后面字符串是sql文的where语句&#xff0c;但要注意要使用%&#xff0c;需要写%% 验证用&#xff1a;导出的表&#xff0c;导入到新的数据库 db的数…

ASP.NET Core 使用 WebClient 从 URL 下载

本文使用 ASP .NET Core 3.1&#xff0c;但它在.NET 5、 .NET 6和.NET 8上也同样适用。如果使用较旧的.NET Framework&#xff0c;请参阅本文&#xff0c;不过&#xff0c;变化不大。 如果想要从 URL 下载任何数据类型&#xff0c;请参阅本文&#xff1a;HttpClient 使用WebC…

快速上手Vim的使用

Vim Linux编辑器-vim使用命令行模式下所有选项都可以带数字底行模式可视块模式&#xff08;ctrlV进入&#xff09; Linux编辑器-vim使用 Vim有多种模式的编辑器。能帮助我们很快的进行代码的编辑&#xff0c;甚至完成很多其他事情。 默认情况下我们打开vim在命令模式下&#x…

nodejs - vue 视频切片上传,本地正常,线上环境导致磁盘爆满bug

nodejs 视频切片上传&#xff0c;本地正常&#xff0c;线上环境导致磁盘爆满bug 原因&#xff1a; 然后在每隔一分钟执行du -sh ls &#xff0c;发现文件变得越来越大&#xff0c;即文件下的mp4文件越来越大 最后导致磁盘直接爆满 排查原因 1、尝试将m3u8文件夹下的所有视…

114,【6】攻防世界 web wzsc_文件上传

进入靶场 传个桌面有的 直接空白了 我们 访问一下上传的东西 /index 没显示用于解析的.htaccess和.user.ini 文件&#xff0c;还两个都不显示 .htaccess 和 .user.ini 文件分别用于 Apache 服务器和 PHP-FPM 环境的目录级配置 但上传的时候bp查看状态码是200&#xff0c;…

Open3d Qt的环境配置

Open3d Qt的环境配置 一、概述二、操作流程2.1 下载文件2.2 新建文件夹2.3 环境变量设置2.4 qt6 引用3、qt中调用4、资源下载一、概述 目前统一使用qt6配置,open3d中可视化功能目前使用vtk代替,语言为c++。 二、操作流程 2.1 下载文件 访问open3d github链接,进入releas…

零基础都可以本地部署Deepseek R1

文章目录 一、硬件配置需求二、详细部署步骤1. 安装 Ollama 工具2. 部署 DeepSeek-R1 模型3. API使用4. 配置图形化交互界面&#xff08;可选&#xff09;5. 使用与注意事项 一、硬件配置需求 不同版本的 DeepSeek-R1 模型参数量不同&#xff0c;对硬件资源的要求也不尽相同。…

Rocky Linux9安装Zabbix7.0(精简版)

Linux 系统版本 Rocky Linux release 9.3 (Blue Onyx) 注意&#xff1a;zabbix 7以上版本不支持CentOS 7系统&#xff0c;需要CentOS 8以上&#xff0c; 本教程支持CentOS9及Rocky Linux 9 在Rocky Linux release 9.3测试通过 Linux环境准备 关闭防火墙和selinux #关闭防…

Qt程序发布

关注后回复 qt 获取相关资料 找到Qt安装目录中的 windeployqt.exe 将其路径添加到Path环境变量中可能会涉及到多平台架构的版本&#xff0c;选择一个目标版本将Release版中的 ***.exe 复制到某空文件夹cmd 进入上述文件夹中执行 windeployqt.exe ***.exe此时会将该 ***.exe 文件…

从O(k*n)到O(1):如何用哈希表终结多层if判断的性能困局

【前言】   本文将以哈希表重构实战为核心&#xff0c;完整展示如何将传统条件匹配逻辑(上千层if-else判断)转化为O(1)的哈希表高效实现。通过指纹验证场景的代码级解剖&#xff0c;您将深入理解&#xff1a;   1.哈希函数设计如何规避冲突陷阱   2.链式寻址法的工程实现…

后端java工程师经验之谈,工作7年,mysql使用心得

mysql 工作7年&#xff0c;mysql使用心得 mysql1.创建变量2.创建存储过程2.1&#xff1a;WHILE循环2.2&#xff1a;repeat循环2.3&#xff1a;loop循环2.4&#xff1a;存储过程&#xff0c;游标2.5&#xff1a;存储过程&#xff0c;有输入参数和输出参数 3.三种注释写法4.case …