在 Javascript 中交换整个数组

Posted

技术标签:

【中文标题】在 Javascript 中交换整个数组【英文标题】:Swap entire arrays in Javascript 【发布时间】:2015-04-15 15:18:39 【问题描述】:

当我尝试创建一个函数来交换 2 个数组时,原始数组保持不变。

function swap(x, y) 
    var temp = x; x = y; y = temp;


u=[1, 0];
v=[0, 1];
swap(u, v);
console.log(u);
console.log(v);

这导致 u 为 [1, 0] 和 v 为 [0, 1]。函数调用 swap 后值没有改变。

另一方面,如果我在没有函数调用的情况下这样做:

u=[1, 0];
v=[0, 1];
var temp = u;
u = v;
v = temp;
console.log(u);
console.log(v);

然后它们被正确交换,u 为 [0, 1],v 为 [1, 0]。

我认为 javascript 数组是按引用传递的,而不是按值传递的。我是不是误会了什么?

【问题讨论】:

预期输出是什么? u 和 v 应该交换。所以 u 应该是 [0, 1] 而 v 应该是 [1, 0]。我也会在问题中更清楚地说明。 数组没有复制这一事实并不意味着它们是“通过引用”传递的。这更类似于 pass-by-pointer。 “我认为 Javascript 数组是通过引用传递的,而不是通过值传递的。我在这里误解了什么吗?”是的,JavaScript 只有 pass-by-value。值的类型无关紧要。 【参考方案1】:

Javascript 无法传递对 uv 变量本身的引用。因此,在swap() 函数中没有分配给xy 将改变分配给uv 的内容。 Javascript 传递对 uv 持有的对象的引用。因此,您不能从swap() 中更改uv 变量。您可以更改它们指向的对象的内容,从而可以修改uv 指向的对象的属性。

由于我有 C/C++ 背景,我想到了 Javascript 在将对象传递为“按指针传递”时的作用。当您调用swap(u, v) 时,传递给swap() 函数的是指向u 也指向的数组的指针。所以,现在你有两个变量 ux 都“指向”同一个数组。因此,如果您修改该实际数组,那么由于u 指向同一个数组,两者都会看到修改。但是,您在 swap() 函数中所做的任何事情都无法改变 uv 实际指向的对象。


在 Javascript 中,更改原始变量指向的对象的唯一方法是使它们成为对象的属性并像这样传递对象:

function swap(obj, x, y) 
    var temp = obj[x]; obj[x] = obj[y]; obj[y] = temp;


var container = ;
container.u = [1, 0];
container.v = [0, 1];
swap(container, "u", "v");
console.log(container.u);
console.log(container.v);

如果您不介意从头开始重写两个数组,您可以将一个数组的所有内容复制到一个临时数组,然后将一个数组复制到另一个数组,然后将临时数组的内容复制回第一个原始数组。这不是很有效,可能有更好的方法来解决您最初的问题,但可以做到。

 function swap(x, y) 
      // remove all elements from x into a temporary array
      var temp = x.splice(0, x.length);
      // then copy y into x
      x.push.apply(x, y);
      // clear y, then copy temp into it
      y.length = 0;
      y.push.apply(y, temp);
 

【讨论】:

我在 C++ 中使用过std::swap()。有没有办法在 Javascript 中实现同样的功能? 这是真正的引用传递。在像C 这样的语言中,您只能按值传递,但您可以按值传递引用,因此您也可以按值传递对引用的引用,这允许 OP 期望的行为 @user3448821 - 在 Javascript 中,模拟真实引用以允许您更改原始变量包含的唯一方法是通过对象的属性生成 uv 并传递包含对象。然后,您可以交换这两个属性的内容。 @user3448821 - 我在使用对象交换的答案中添加了一个示例。 @Paulpro - 我试图清理我在回答您的评论时使用的术语。【参考方案2】:

了解这些“参考/价值”问题的术语很棘手,但我会尽力而为。

您的对象/数组变量实际上只是引用。与 C++ 不同,说“x = y”实际上并不会将对象的变量复制到新的内存位置。在内部,它只是复制一个指针位置。该语言没有在新实例中“自动重新创建”对象或数组之类的结构;如果出于某种原因,您想要维护数组的两个副本,则需要显式创建它然后复制值(即,= [];= new Array(); 或像 = oldArray.map(...) 这样的复制函数)

一个可能在概念上有所帮助的小代码示例。这些相同的规则适用于对象和数组。

a = ; // In case you haven't seen it, this is like shorthand of "new Object();"
b = ;
c = b;
console.log(a === b); // false
console.log(b === c); // true
b.property = "hello";
console.log(c.property) // hello

【讨论】:

“与 C++ 不同,说“x = y”实际上并不会将对象的变量复制到新的内存位置。”它像 C++ -- 在 C++ 中它会是 Foo *y = ...; Foo *x = y; 结构是一样的。 @newacct 当然,我认为我们用不同的词汇说同样的话,但我不确定我是否同意相似之处。在您的两行示例中,您声明了一个包含指针的变量。然后,您正在创建另一个变量,并且从我的角度来看,将该指针复制到新的内存位置。编译器必须为那些新的 32/64 位分配。因此,如果您总是只使用每个对象的指针,那就像 C++ 一样(我理解这是一种危险的做法,只是不必要地应用到任何地方) 但这正是 JavaScript 代码所说的。类型只是没有用 JavaScript 写出来。【参考方案3】:

就像 Java 一样,JavaScript 是仅按值传递。分配给函数中的局部变量永远不会对函数之外的任何内容产生任何影响。

【讨论】:

【参考方案4】:

它们是通过引用传递的,但它们也是通过引用分配的。当您编写x = y 时,您并没有修改任何一个数组,您只是让您的局部变量x 引用与y 相同的数组。

如果要交换数组内容,则必须自己修改数组:

function swap(x,y) 
    var temp = x.slice(0);
    x.length = 0;
    [].push.apply( x, y );
    y.length = 0;
    [].push.apply( y, temp ); 

【讨论】:

明确一点,这并不是真正的交换。这是从头开始重写两个数组并且不是特别有效。也许这就是 OP 想要的,但我肯定不会将其称为通常意义上的交换。 @jfriend00 同意。这个答案是Θ(n),而您使用容器对象的解决方案是Θ(1)。它还使用额外的内存,因为它复制x。对于小型阵列,它们的速度大致相同,但对于大型阵列,您的速度肯定会超过这个速度。我会说这个版本更容易阅读和使用。 它们不是通过引用传递的。语义与 Java 中的语义完全相同。 Java 在 Stack Overflow 上被普遍描述为没有引用传递。

以上是关于在 Javascript 中交换整个数组的主要内容,如果未能解决你的问题,请参考以下文章

Java-选择排序算法

选择排序

为什么switch在Javascript数组的for循环中只运行一次?

javascript 交换数组中的项目

排序算法-选择排序

一维数组中的任意二元素交换最多只能将整个序列的逆序对数减少1