-1是一个神奇的数字? 反模式? 代码味道? 当局的报价和指南

可能重复:
不断滥用?

我已经看到-1在各种API中使用,最常见的是搜索具有从零开始的索引的“集合”时,通常用于指示“未找到”索引。 这“有效”,因为-1从来都不是合法的索引。 似乎任何负数都应该有效,但我认为-1几乎总是被用作某种(不成文的?)约定。

我想至少暂时将范围限制为Java。 我的问题是:

  • 关于使用-1作为这样的“特殊”返回值,Sun的官方文字是什么?
  • 关于这个问题有哪些引用,例如James Gosling,Josh Bloch,甚至是Java之外的其他权威人士?
  • 过去有关这个问题的一些值得注意的讨论是什么?

这是一种常见的语言,其中的类型不包括范围检查。 “越界”值用于表示几种条件之一。 这里,返回值表示两件事:1)是找到的字符,2)它在哪里找到。 使用-1表示not found ,而非负索引表示简洁地将这两个索引编码为一个值, not-found的事实不需要返回索引。

在具有严格范围检查的语言中,例如Ada或Pascal,该方法可以实现为(伪代码)

  bool indexOf(c:char, position:out Positive); 

Positive是int的子类型,但仅限于非负值。

这将找到/未找到的标志与位置分开。 该位置作为输出参数提供 – 基本上是另一个返回值。 它也可以是一个输入输出参数,从给定位置开始搜索。 这里不允许使用-1表示not-found,因为它违反了Positive类型的范围检查。

java中的替代方案是:

  • 抛出exception:这不是一个好的选择,因为找不到一个字符并不是一个例外情况。
  • 将结果拆分为多个方法,例如boolean indexOf(char c); int lastFoundIndex(); boolean indexOf(char c); int lastFoundIndex(); 。 这意味着对象必须保持状态,这在并发程序中不起作用,除非状态存储在线程本地存储中,或者使用同步 – 所有相当大的开销。
  • 返回位置并分别找到标志:例如boolean indexOf(char c, Position pos) 。 这里,创建位置对象可能被视为不必要的开销。
  • 创建一个多值返回类型

 class FindIndex { boolean found; int position; } FindIndex indexOf(char c); 

虽然它明确地区分了返回值,但它会受到对象创建开销的影响。 其中一些可以通过将FindIndex作为参数传递来减轻,例如

 FindIndex indexOf(char c, FindIndex start); 

顺便提一下,多个返回值将成为java(oak)的一部分,但是在1.0之前被削减以缩短发布时间。 詹姆斯戈斯林说他希望他们被包括在内。 它仍然是一个希望的function 。

我的看法是使用魔术值是在单个返回值中编码多值结果(标志和值)的实用方法,而不需要过多的对象创建开销。

但是,如果使用魔术值,如果它们在相关的api调用中保持一致,则使用它会更好。 例如,

  // get everything after the first c int index = str.indexOf('c'); String afterC = str.substring(index); 

Java在这里不足,因为在调用substring时使用-1将导致IndeOutOfBoundsException 。 相反,如果将负值视为从字符串末尾开始,则在使用-1调用时,子字符串返回“”可能更加一致。 对错误条件的魔术值的批评者说,可以忽略返回值(或假设为正数)。 以有用的方式处理这些魔术值的一致api将减少检查-1并允许更清晰的代码的需要。

-1是一个神奇的数字?

在这种情况下,不是真的。 关于-1没有什么特别之处……除了因为否定而保证它是无效的索引值这一事实。

反模式?

不能。要获得反模式的资格,就需要对这个成语有害。 我认为这种方式使用-1无害。

代码味道?

同上。 (使用命名常量而不是裸-1文字可以说是更好的风格。但我不认为这就是你所要求的,而且无论如何它都不会算作“代码味道”,IMO。)

当局的报价和指南

不是我知道的。 但是,我会观察到这个“设备”用于各种标准类。 例如, String.indexOf(...)返回-1表示无法找到字符或子字符串。


就我而言,这只是一种在某些情况下有用的“算法设备”。 我敢肯定,如果你回顾一下文献,你会看到使用-1 (或0代表基于一个arrays的语言)的例子,这种方式可以追溯到1960年代和之前。

在这种情况下,选择-1而不是其他一些负数仅仅是个人品味的问题,并且(IMO)不值得分析。


方法返回-1 (或其他值)以指示错误而不是抛出exception可能是个坏主意。 但是,这里的问题不是返回的值,而是该方法要求调用者明确测试错误的事实。

另一方面,如果由-1 (或其他)表示的“条件” 不是 “错误”/“exception条件”,则返回特殊值既合理又合适。

当找不到索引时,Java和JavaScript都使用-1 。 由于索引始终为0-n因此它似乎是一个非常明显的选择。

 //JavaScript var url = 'example.com/foo?bar&admin=true'; if(url.indexOf('&admin') != -1){ alert('we likely have an insecure app!'); } 

我发现这种方法(我在扩展Array类型元素时使用.indexOf()方法)非常正常。

另一方面,您可以尝试PHP方法,例如strpos(),但恕我直言,它会让人感到困惑,因为有多种返回类型(未找到时返回FALSE)

-1作为返回值稍微丑陋但必要。 发出“未找到”情况的替代方案是恕我直言,更糟糕的是:

  • 您可以抛出exception,但这并不理想,因为exception最适合用于指示需要某种forms的恢复或传播失败的意外情况。 实际上并不期望找到子串的出现。 此外,exception抛出会对性能造成严重影响。

  • 您可以将复合结果对象与(found,index)一起使用,但这需要对象分配和调用者方面更复杂的代码来检查结果。

  • 您可以为contains和indexOf分离出两个单独的函数调用 – 但是对于调用者来说这又是非常麻烦的,并且还会导致性能损失,因为两个调用都是O(n)并且需要完全遍历String。

就个人而言,我从不喜欢引用-1常量:我对未找到的测试总是如下:

 int i = someString.indexOf("substring"); if (i>=0) { // do stuff with found index } else { // handle not found case } 

最好为代码中的所有常量值定义最终的类变量。 但是在没有显式声明的情况下使用0,1,-1,“”(空字符串)是普遍接受的。

这是C的inheritance,其中只能返回单个原始值。 在java中,您还可以返回单个对象。

因此,对于新代码,返回basetype的对象,其子类型指示要与instaceof一起使用的问题,或者抛出“未找到”exception。

对于现有的特殊值,因此在代码名称中使-1成为常量 – NOT_FOUND – 因此读者可以在不必检查javadoc的情况下判断含义。

null相同的做法适用于-1 。 它已被多次讨论过。

例如Java api设计 – NULL或Exception

它的使用是因为它是在基于0的数组中遇到的第一个无效值。 如你所知,并非所有类型都可以保留null或什么都没有,所以需要“某些东西”来表示什么。

我会说它不是官方的,它刚刚成为惯例(不成文),因为它对情况非常敏感。 就个人而言,我也不会称之为问题。 API设计也取决于作者,但可以在线找到指南 。

据我所知,这些值称为sentinel值,尽管大多数常见定义与此场景略有不同。

像Java这样的语言选择不支持通过引用传递(我认为这是个好主意),因此虽然各个参数的值是可变的,但传递给函数的变量仍然不受影响。 因此,您只能有一个返回值只有一种类型。 所以你要做的是选择一个无效的有效类型的值,并返回它来传输额外的语义,因为返回值实际上不是操作的返回值而是一个特殊的信号。

现在我想,最干净的方法是使用一个contains和一个indexOf方法,如果您要求的元素不在集合中,则第二个方法会引发exception。 为什么? 因为人们会期望以下是真实的:

 someCollection.objectAtIndex(someCollection.indexOf(someObject)) == someObject 

你可能得到的是一个例外,因为-1超出界限,而这个似是而非的关系不正确的实际原因是, someObject不是someCollection的元素,这就是为什么内部调用应该提高例外。

现在虽然干净又坚固,但它有两个关键缺陷:

  • 通常这两个操作通常都会花费你O(n)(除非你在集合中有一个逆映射),所以如果你只做一个,你会更好。
  • 这真的很啰嗦。

最后,由你来决定。 这是一个哲学问题。 我称之为“语义黑客”,以牺牲稳健性为代价来实现短缺和速度。 你的来电 ;)

格尔茨
back2dos

为什么51%意味着公司股东之间的一切,因为它是最好的,最有意义而不是-2或-3 ……