我可以使用实例化的对象作为数组键吗?

Posted

技术标签:

【中文标题】我可以使用实例化的对象作为数组键吗?【英文标题】:Can I use an instantiated Object as an Array Key? 【发布时间】:2011-06-06 07:00:06 【问题描述】:

例如:

$product = new Product("cat");

if(isset($sales[$product]))
     $sales[$product]++;

else
     $sales[$product] = 1;

【问题讨论】:

【参考方案1】:

来自docs:

数组和对象不能用作键。这样做会导致警告:Illegal offset type。

您可以给每个实例一个唯一的 ID 或覆盖 __toString() 以便它返回一些唯一的东西并执行例如

$array[(string) $instance] = 42;

【讨论】:

【参考方案2】:

您可以使用http://www.php.net/manual/en/class.splobjectstorage.php

$product = new Product("cat");
$sales = new SplObjectStorage();
if(isset($sales[$product]))
     $sales[$product]++;

else
     $sales[$product] = 1;

它不是一个真正的数组,但具有相当数量的类似数组的功能和语法。然而,由于它是一个对象,由于它奇怪的 foreach 行为以及它与所有本机 php 数组函数的不兼容,它在 php 中的行为就像一个不合适的人选。有时您会发现通过

将其转换为真实数组很有用
$arr = iterator_to_array($sales);

所以它与你的代码库的其余部分配合得很好。

【讨论】:

【参考方案3】:

有一个spl_object_hash函数用于获取唯一的对象id作为字符串,可以用作数组键。 http://php.net/manual/en/function.spl-object-hash.php

【讨论】:

实际上来自文档:当一个对象被销毁时,它的 hash 可能会被其他对象重用。因此,通常最好按照 goat 的建议使用 SplObjectStorage。【参考方案4】:

仅限integers and strings are allowed as array keys。如果您绝对需要该功能,您可以编写一个实现 ArrayAccess 的类。

【讨论】:

它不允许您将对象用作数组键。它唯一允许的是将对象视为数组,因此对象的属性将是键 允许您使用对象作为键。有关示例实现,请参阅 pastebin.com/fzqeFswJ。 我明白你的意思,这是有道理的。我的意思是它不允许您将对象用作 array 键。但是是的,它将允许您使用对象作为 ArrayAccess 对象的键【参考方案5】:

如果对象是使用new stdClass() 制作的简单预定义类,则可以使用带有json_encode 的此类的json 表示形式。

$product = new stdClass();
$product->brand = "Acme";
$product->name = "Patator 3.14";

$product_key = json_encode($product);

if(isset($sales[$product_key]))
     $sales[$product_key]++;

else
    $sales[$product_key] = 1;

但请记住,两个对象的相等性始终是一种商业模式选择,必须仔细设计。

【讨论】:

【参考方案6】:

你可以有两个数组:

Array 1 contains the keys:   |  Array 2 contains the values
+--------+-------------+     |  +--------+------------+
| index: | value:      |     |  | index: | value:     |
| 0      | Object(key) |     |  | 0      | sth(value) |
| 1      | Object(key) |     |  | 1      | sth(value) |
+--------+-------------+     |  +--------+------------+

您在数组 1 中搜索对象, 然后你选择那个对象的索引 将其用作数组 2 的索引和 => 获取价值

在php代码中

public function getValue($ObjectIndexOfYourArray)
  foreach(array1 as $key => $value) 
    if($value == ObjectIndexOfYourArray)
      return array2[$key];
    
  

希望对你有帮助

【讨论】:

【参考方案7】:

我知道这个问题已经过时了,而且 SplObjectStorage 有一些奇怪的行为(例如在循环使用 foreach 时)。

截至今天,我编写了一个名为 linked-hash-map (https://github.com/tonix-tuft/linked-hash-map) 的库,它在 PHP 中实现了关联数组/哈希映射/哈希表,并允许您使用任何 PHP 数据类型作为键:

<?php

use LinkedHashMap\LinkedHashMap;

$map = new LinkedHashMap();
$map[true] = 'bool (true)';
$map[false] = 'bool (false)';
$map[32441] = 'int (32441)';
$map[-32441] = 'int (-32441)';
$map[2147483647] = 'int (2147483647)';
$map[-2147483648] = 'int (-2147483648)';
$map[PHP_INT_MAX - 100] = 'int (PHP_INT_MAX - 100)';
$map[PHP_INT_MIN] = 'int (PHP_INT_MIN)';
$map[0.5] = 'float/double (0.5)';
$map[-0.5] = 'float/double (-0.5)';
$map[123891.73] = 'float/double (123891.73)';
$map[-123891.73] = 'float/double (-123891.73)';
$map[PHP_INT_MAX + 10] = 'float/double (PHP_INT_MAX + 10)';
$map[PHP_INT_MIN - 10] = 'float/double (PHP_INT_MIN - 10)';
$map['abc'] = 'string (abc)';
$map["abcdef"] = "string (abcdef)";
$map['hfudsh873hu2ifl'] = "string (hfudsh873hu2ifl)";
$map["The quick brown fox jumps over the lazy dog"] =
  'string (The quick brown fox jumps over the lazy dog)';
$map[[1, 2, 3]] = 'array ([1, 2, 3])';
$map[['a', 'b', 'c']] = "array (['a', 'b', 'c'])";
$map[[1, 'a', false, 5, true, [1, 2, 3, ['f', 5, []]]]] =
  "array ([1, 'a', false, 5, true, [1, 2, 3, ['f', 5, []]]])";

class A 

$objA = new A();
$map[$objA] = "object (new A())";

// You can even use file handles/resources:
$fp = fopen(__DIR__ . '/private_local_file', 'w');
$map[$fp] = "resource (fopen())";

$ch = curl_init();
$map[$ch] = "resource (curl_init())";

// All the values can be retrieved later using the corresponding key, e.g.:
var_dump($map[[1, 2, 3]]); // "array ([1, 2, 3])"
var_dump($map[$objA]); // "object (new A())"
var_dump($map[$ch]); // "resource (curl_init())"

【讨论】:

【参考方案8】:

您终于可以在 PHP 8.0+ 中使用对象作为数组键。但这是一个谎言。它不会是您使用的常规数组,而是the new WeakMap class。

声明一个new WeakMap();,然后你可以把它装满对象作为键。与任何基于Spl 的技巧相比,使用它的优势在于WeakMap 在内存泄漏方面更好。 “弱”部分是指对象在映射中的引用是“弱”的,并且一旦对象不再在范围内,就不会阻止该对象被垃圾收集。 WeakMap 会自动从自身移除对象。

这里是 a good run-through 的用例。

PHP RFC 也不错。

【讨论】:

以上是关于我可以使用实例化的对象作为数组键吗?的主要内容,如果未能解决你的问题,请参考以下文章

带有 lambda 作为每个实例化的唯一默认参数的模板

Django:RelatedManager 对象是如何实例化的?

你可以在实例化后向 React JSX 元素添加一个键吗?

为实例化的表单对象捕获 onClose 事件

更改使用 loadNibNamed 实例化的类

获取其他玩家实例化的游戏对象 Unity Photon