Java 字符串插入和文字字符串声明的搜索成本

Java 字符串插入和文字字符串声明的搜索成本,java,performance,search,string-literals,string-interning,Java,Performance,Search,String Literals,String Interning,两个问题 当我们声明文本字符串时,我们搜索堆的字符串池中是否存在相同的字符串这也是一种实习方式吗(实习类String)? 在我看来,每个文本字符串声明都需要一个二进制搜索,所以当n是池中现有字符串的数量时,它至少要花费log(n)。如果池中有很多字符串,那么成本可能会很高。(可能是搜索成本和内存的折衷?)从这个角度来看,声明mant文本字符串可能是危险的。 这种搜索成本有多大,以及为什么java是这样设计的(声明文字字符串时搜索池)。 以下是我所说的理解背景 各国: 字符串是常量;它们的值在创

两个问题

  • 当我们声明文本字符串时,我们搜索堆的字符串池中是否存在相同的字符串这也是一种实习方式吗(实习类
    String
    )?

  • 在我看来,每个文本字符串声明都需要一个二进制搜索,所以当n是池中现有字符串的数量时,它至少要花费log(n)。如果池中有很多字符串,那么成本可能会很高。(可能是搜索成本和内存的折衷?)从这个角度来看,声明mant文本字符串可能是危险的。 这种搜索成本有多大,以及为什么java是这样设计的(声明文字字符串时搜索池)。

  • 以下是我所说的理解背景


    各国:

    字符串是常量;它们的值在创建后无法更改。字符串缓冲区支持可变字符串。因为字符串对象是不可变的,所以可以共享它们

    评论:

    换句话说,因为编译器知道字符串的原始值一旦创建就不能更改,所以它可以安全地使用现有数据,并避免重复数据造成内存混乱


    您混淆了编译时复杂性和运行时复杂性

    当类被加载时,它会进行搜索以查看每个文本是否已经存在(尽管我认为它会使用hashmap进行O(1)查找,而不是您的建议)

    当代码运行时,它在内存中有对字符串的引用,因此没有比非文本更大的额外开销

    所以是的,文字被拘留了。根据字符串的Javadoc

    最初为空的字符串池由类字符串私下维护

    您可以对字符串调用
    intern()
    ,将其添加到此池中。逻辑上,如果
    a.equals(b)
    那么
    a.intern()==b.intern()
    ,因为
    .intern()
    保证从唯一池返回

    例如:

    class InternTest {
        // assuming InternTest is the only class, internPool.size = 0
        String x = "ABC"; // interned at class load, internPool.size = 1
        String y = "DEF"; // interned at class load, internPool.size = 2
        String z = "ABC"; // interned at class load, but match found - size = 2 still
    
        void foo() {
            // random int is just a mechanism to get something that I know won't
            // be interned at loadtime - could have loaded from file or database too
            int i = (new java.util.Random()).nextInt(1000) + 100;
            int j = i;
            String s = String.valueOf(i); // not yet interned, size = 2 still
            String t = String.valueOf(j); // not yet interned, size = 2 still
    
            String sIntern = s.intern(); // manually interned, size = 3 now
            String tIntern = t.intern(); // manually interned, match found, size = 3 still
    
            System.out.println("equals: " + (s.equals(t))); // should be true
            System.out.println("== raw: " + (s == t)); // should be false, different variables
            System.out.println("== int: " + (sIntern == tIntern)); // should be true, from unique pool
    
           System.out.println("x and z: " + (x == z)); // should be true, interned at class load
        }
    
        public static void main(String[] args) {
            (new InternTest()).foo();
        }
    
    }
    
    运行时的结果:

    C:\Documents and Settings\glowcoder\My Documents>java InternTest
    equals: true
    == raw: false
    == int: true
    x and z: true
    
    我应该指出,这种假设永远不会成立。Java语言本身就有许多
    String
    s,在我们的
    String
    s有机会看到曙光之前,这些都将被实习。但是,假设所有的东西都是按顺序加载的,如果你只考虑字符串的增量,并且假设与现有的实习生没有冲突(我们都知道实习生会很挑剔,而且充满戏剧性,对吗?窃笑),那么这些数字确实表明了字符串池大小的增量。

    1-当我们声明文本字符串时,我们搜索堆的字符串池中是否存在相同的字符串。这也是一个interning(类字符串的方法intern)

    对。这个过程叫做实习。然而,它只发生一次。。。加载包含文本的类时

    2-在我看来,每个文本字符串声明都需要一个二进制搜索,所以当n是池中现有字符串的数量时,它至少要花费log(n)

    不,没有。池是一个哈希表

    。。。如果池中有很多字符串,那么成本可能会很高

    不会的。在字符串池哈希表中查找的成本是
    O(1)

    。。。从这个角度来看,声明许多文本字符串可能是危险的

    与加载然后JIT编译类文件的其他成本相比,该成本并不显著。声明大量的文本字符串并没有与性能相关的“危险”

    显然,与字符串文字相对应的字符串对象“永久”占用内存,您通常不希望不必要地浪费内存。但如果需要使用这些常量字符串,则必须以某种方式表示它们。而其他表示它们的方法要么以其他方式使用内存,要么涉及其他运行时成本;e、 g.从文件中读取或从数据库中检索的成本

    插入字符串文本的好处是堆不会被同一个文本字符串的多个副本弄得乱七八糟。对于典型的SE/EE应用程序来说,这可能并不重要,但对于ME平台来说,堆内存是非常昂贵的,浪费它是一件坏事


    @雷诺询问字符串被拘留的次数。有两种情况:

    • 显式调用
      String.intern()
      的次数与应用程序选择的次数相同(或尽可能少)

    • 对于字符串文本,
      javac
      编译器将确保给定的
      .class
      文件的常量池中不包含任何字符串文本的多个副本。这意味着一个在很多地方都有一个给定文本的类在加载该类时只会导致该文本被实习一次。但是,如果在各自的源代码中有两个具有相同文本字符串的类,则它们在各自的常量池中都具有字符串值,并且在加载各自的类时都会插入字符串


    实际上,字符串实习确实发生在运行时(加载类时)。但是它只在每个文本字符串中发生一次,复杂性是
    O(1)
    ,所以这不是性能问题。当我考虑它时,这是有意义的-如果没有JVM加载,我们如何用Interment保存HashMap?此外,由于
    intern()
    是本机方法,因此无法在编译时执行。我会相应地更新我的答案。谢谢谢谢你的快速回答!我还有一些问题。。在您的回答中,唯一的池,哪一个是唯一的意思:1)池中的每个元素都是唯一的2)池是唯一的。如果在编译时有2个元素,然后在运行时声明第三个字符串而不进行内部调用,那么第三个字符串也将进入同一个池?第一个问题