我刚开始读《雄辩的Ruby》这本书,我已经读到了关于Ruby中的符号的那一章。
Ruby中的字符串是可变的,这意味着每个字符串分配内存,因为内容可以改变,即使内容是相等的。如果我在Java中需要一个可变字符串,我会使用StringBuffer。然而,由于常规Java字符串是不可变的,一个字符串对象可以被多个引用共享。因此,如果我有两个内容为"Hello World"的常规字符串,则两个引用将指向同一个对象。
那么,Ruby中符号的目的实际上与Java中的"正常"字符串对象相同吗?它是一个赋予程序员优化内存的功能吗?
我在这里写的是真的吗?还是我误解了符号的概念?
符号在Ruby中与字符串很接近,但它们并不等同于普通的Java字符串,尽管它们也有一些共性,比如不变性。但是有一个细微的区别——有不止一种方法可以获得对Symbol的引用(稍后会详细介绍)。
在Ruby中,完全可以来回转换两者。String#to_sym用于将字符串转换为符号,Symbol#to_s用于将符号转换为字符串。那么区别是什么呢?
为Symbol引用RDoc:
在程序执行期间,将为给定的名称或字符串创建相同的Symbol对象,而不考虑该名称的上下文或含义。
符号是唯一标识符。如果Ruby解释器第一次被:mysymbol
绊倒,会发生这样的事情:在内部,如果符号还不存在,它会被存储在一个表中(很像解析器使用的"符号表";这是使用C函数rb_intern
(在CRuby/MRI中)来实现的,否则Ruby将查找表中的现有值并使用它。在符号被创建并存储在表中之后,从那时起,无论你在哪里引用符号:mysymbol
,你都会得到相同的对象,即存储在该表中的对象。
考虑这段代码:
sym1 = :mysymbol
sym2 = "mysymbol".to_sym
puts sym1.equal?(sym2) # => true, one and the same object
str1 = "Test"
str2 = "Test"
puts str1.equal?(str2) # => false, not the same object
注意到差异。它说明了Java字符串和Ruby符号之间的主要区别。如果你想要Java中String的对象相等,你只有在比较String的完全相同的引用时才能实现,而在Ruby中,就像你在上面的例子中看到的那样,可以通过多种方式获得对Symbol的引用。
符号的唯一性使它们成为哈希中的完美键:与常规字符串相比,查找性能得到改善,因为您不必显式地哈希您的键,因为它将被字符串所要求,您可以简单地使用符号的唯一标识符直接查找。通过写:somesymbol
,你告诉Ruby"给我一个你存储在标识符"某个符号"下的东西"。因此,当您需要唯一标识事物时,符号是您的首选,例如:
- 哈希键
- 命名或引用变量、方法和常量的名称(例如:obj。send:method_name)
但是,正如Jim Weirich在下面的文章中指出的那样,符号不是字符串,甚至不是鸭子打字意义上的字符串。您不能连接它们或检索它们的大小或从中获取子字符串(除非先将它们转换为字符串)。因此,何时使用字符串的问题很简单——正如Jim所说:
使用字符串当你需要…嗯…类似字符串的行为。
关于这个话题的一些文章:
- Ruby符号
- 。
- 符号不是不可变字符串 查看Ruby符号的13种方法
不同的是,如果Java字符串包含相同的文本,则它们不需要指向相同的对象。在代码中声明常量字符串时,通常会出现这种情况,因为编译器会将其放入常量池中。
但是,如果在Java运行时动态地创建一个String,两个String完全可以指向不同的对象,并且仍然包含相同的文本。不过,你可以通过内部化String对象(调用String.intern())来强制实现这一点,参见Java API