【UE4从零开始 011】容器之TMap

TMap 也是是虚幻引擎中最常用的容器类。与TArray很相似,不过TMap是以键值对<Key, Value>的形式存在的。

1、创建

	TMap<int32, FString> FruitMap;

2、填充数据

AddEmplace

填充Map的标准方式是使用 Add 函数:

	FruitMap.Add(5, TEXT("Banana"));
	FruitMap.Add(2, TEXT("Grapefruit"));
	FruitMap.Add(7, TEXT("Pineapple"));
	// FruitMap == [
	//  { Key:5, Value:"Banana"     },
	//  { Key:2, Value:"Grapefruit" },
	//  { Key:7, Value:"Pineapple"  }
	// ]

虽然这里我们是按(5、2、7)这样的顺序填充的数据,但无法保证这些元素就是按照这样的顺序排列,因为 TMap 是无序的。

TMap 的 Key 是唯一的,当我们添加一个已经存在的 Key 时,它会覆盖原有的 Value :

FruitMap.Add(2, TEXT("Pear"));
// FruitMap == [
//  { Key:5, Value:"Banana"    },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" }
// ]

TMultiMap 的 Key 不是唯一的,可以多次添加同一个 Key 的 <Key, Value>。

当我们调用 Add 时,只传入参数 Key,不传 Value 的话,会使用 Value 的默认构造函数来创建对象:

FruitMap.Add(4);
// FruitMap == [
//  { Key:5, Value:"Banana"    },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:""          }
// ]

与 TArray 一样,也可以使用 Emplace 来避免添加过程中创建临时变量:

FruitMap.Emplace(3, TEXT("Orange"));
// FruitMap == [
//  { Key:5, Value:"Banana"    },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:""          },
//  { Key:3, Value:"Orange"    }
// ]

Append

同 TArray 一样,我们可以使用 Append 来一次性添加多个元素:

TMap<int32, FString> FruitMap2;
FruitMap2.Emplace(4, TEXT("Kiwi"));
FruitMap2.Emplace(9, TEXT("Melon"));
FruitMap2.Emplace(5, TEXT("Mango"));
FruitMap.Append(FruitMap2);
// FruitMap == [
//  { Key:5, Value:"Mango"     },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:"Kiwi"      },
//  { Key:3, Value:"Orange"    },
//  { Key:9, Value:"Melon"     }
// ]

相当于使用 Add/Emplace 逐个添加,同样会覆盖已存在的键值对。

3、遍历

遍历 TMap 与遍历 TArray类似。
使用C++的 ranged-for’

	for (auto& Elem : FruitMap)
	{
		UE_LOG(LogClass, Log, TEXT("Key: %d   Value: %s"), Elem.Key, *Elem.Value);
	}
	// Output:
	//Key: 5   Value : Mango
	//Key: 2   Value : Pear
	//Key: 7   Value : Pineapple
	//Key: 4   Value : Kiwi
	//Key: 3   Value : Orange
	//Key: 9   Value : Melon

使用迭代器 CreateIteratorCreateConstIterator

	for (auto It = FruitMap.CreateConstIterator(); It; ++It)
	{
		UE_LOG(LogClass, Log, TEXT("Key: %d   Value: %s"), It.Key(), *It.Value());
	}
	// Output:
	//Key: 5   Value : Mango
	//Key: 2   Value : Pear
	//Key: 7   Value : Pineapple
	//Key: 4   Value : Kiwi
	//Key: 3   Value : Orange
	//Key: 9   Value : Melon

4、查询相关

Num 函数获取TMap保存的元素数量:

int32 Count = FruitMap.Num();
// Count == 6

与访问TArray类似,我们可以使用Key的索引来获取对应的Value:

FString Val7 = FruitMap[7];
// Val7 == "Pineapple"
//FString Val8 = FruitMap[8]; // assert!

如果对应的 <Key, Value>不存在,则会触发运行时断言。

使用 Contains 函数检查 TMap 中是否存在特定键:

bool bHas7 = FruitMap.Contains(7);
bool bHas8 = FruitMap.Contains(8);
// bHas7 == true
// bHas8 == false

通常我们可以使用 **操作符“[ ]” 或 Contains 函数来查找 TMap 是否存在某个 Key,但这不是最优的,因为它们会对 Key 进行两次查找。而 Find 函数只执行一次查找,返回的是指向元素的指针,若不存在则返回 null :

FString* Ptr7 = FruitMap.Find(7);
FString* Ptr8 = FruitMap.Find(8);
// *Ptr7 == "Pineapple"
//  Ptr8 == nullptr

FindOrAdd 函数将会搜索对应的 <Key, Value> 是否存在,如果存在则返回 Value 的引用,如果不存在,则会以默认构造函数添加 <Key, Value>,并返回 Value 的引用:

FString& Ref7 = FruitMap.FindOrAdd(7);
// Ref7     == "Pineapple"
// FruitMap == [
//  { Key:5, Value:"Mango"     },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:"Kiwi"      },
//  { Key:3, Value:"Orange"    },
//  { Key:9, Value:"Melon"     }
// ]
FString& Ref8 = FruitMap.FindOrAdd(8);
// Ref8     == ""
// FruitMap == [
//  { Key:5, Value:"Mango"     },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:"Kiwi"      },
//  { Key:3, Value:"Orange"    },
//  { Key:9, Value:"Melon"     },
//  { Key:8, Value:""          }
// ]

因为可能需要执行 Add 操作,所以不能对常量TMap调用此函数。另:如果调用 FruitMap.FindOrAdd(8); 时,发生了内存重新分配,则可能导致 引用“Ref7” 失效。

FindRef 函数将会搜索对应的 <Key, Value> 是否存在,如果存在则返回 Value的值(虽然名字是“Ref”,但返回的是值的副本,不是引用),如果不存在,则返回以默认构造函数创建的值,但不会修改 TMap:

FString Val7 = FruitMap.FindRef(7);
FString Val6 = FruitMap.FindRef(6);
// Val7     == "Pineapple"
// Val6     == ""
// FruitMap == [
//  { Key:5, Value:"Mango"     },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:"Kiwi"      },
//  { Key:3, Value:"Orange"    },
//  { Key:9, Value:"Melon"     },
//  { Key:8, Value:""          }
// ]

FindKey 函数是反向查找(即根据 “Value” 查找“Key”),由于Value可能不是唯一的,索引返回的Key可能是任意对应的一个Key:

const int32* KeyMangoPtr   = FruitMap.FindKey(TEXT("Mango"));
const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat"));
// *KeyMangoPtr   == 5
//  KeyKumquatPtr == nullptr

GenerateKeyArrayGenerateValueArray 分别是使用所有的 Key 和 Value 生成TArray,这两种情况,都会将TArray之前的数据清空:

TArray<int32>   FruitKeys;
TArray<FString> FruitValues;
FruitKeys.Add(999);
FruitKeys.Add(123);
FruitMap.GenerateKeyArray  (FruitKeys);
FruitMap.GenerateValueArray(FruitValues);
// FruitKeys   == [ 5,2,7,4,3,9,8 ]
// FruitValues == [ "Mango","Pear","Pineapple","Kiwi","Orange","Melon","" ]

5、移除

Remove

FruitMap.Remove(8);
// FruitMap == [
//  { Key:5, Value:"Mango"     },
//  { Key:2, Value:"Pear"      },
//  { Key:7, Value:"Pineapple" },
//  { Key:4, Value:"Kiwi"      },
//  { Key:3, Value:"Orange"    },
//  { Key:9, Value:"Melon"     }
// ]

FindAndRemoveChecked

FindAndRemoveChecked 函数从TMap中移除并返回其值,如果不存在对应的Key,则会触发运行时断言:

	FString Removed7 = FruitMap.FindAndRemoveChecked(7);
	// Removed7 == "Pineapple"
	// FruitMap == [
	//  { Key:5, Value:"Mango"  },
	//  { Key:2, Value:"Pear"   },
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:3, Value:"Orange" },
	//  { Key:9, Value:"Melon"  }
	// ]

	//FString Removed8 = FruitMap.FindAndRemoveChecked(8); // assert!

RemoveAndCopyValue

RemoveAndCopyValue 函数会复制移除的元素的Value到传入的参数中,并且返回bool值来表明是否找到对应的键值对,如果没找到则会返回false,不会发生运行时错误:

FString Removed;
bool bFound2 = FruitMap.RemoveAndCopyValue(2, Removed);
// bFound2  == true
// Removed  == "Pear"
// FruitMap == [
//  { Key:5, Value:"Mango"  },
//  { Key:4, Value:"Kiwi"   },
//  { Key:3, Value:"Orange" },
//  { Key:9, Value:"Melon"  }
// ]
bool bFound8 = FruitMap.RemoveAndCopyValue(8, Removed);
// bFound8  == false
// Removed  == "Pear", i.e. unchanged
// FruitMap == [
//  { Key:5, Value:"Mango"  },
//  { Key:4, Value:"Kiwi"   },
//  { Key:3, Value:"Orange" },
//  { Key:9, Value:"Melon"  }
// ]

与 TArray 一样,可以使用 Empty 函数来移除所有元素。

6、排序

可以分别使用 KeySortValueSort 函数来按 Key 或 Value 来进行排序。但TMap的排序是不稳定的,当对TMap修改时很可能会导致TMap的排序被打乱:

FruitMap.KeySort([](int32 A, int32 B) {
    return A > B; // sort keys in reverse
});
// FruitMap == [
//  { Key:9, Value:"Melon"  },
//  { Key:5, Value:"Mango"  },
//  { Key:4, Value:"Kiwi"   },
//  { Key:3, Value:"Orange" }
// ]

FruitMap.ValueSort([](const FString& A, const FString& B) {
    return A.Len() < B.Len(); // sort strings by length
});
// FruitMap == [
//  { Key:4, Value:"Kiwi"   },
//  { Key:5, Value:"Mango"  },
//  { Key:9, Value:"Melon"  },
//  { Key:3, Value:"Orange" }
// ]

7、运算符

和TArray一样,TMap也可以通过标准复制构造函数或赋值运算符复制。同样TMap复制也是深度复制,所有新的TMap将拥有其自身的元素副本:

TMap<int32, FString> NewMap = FruitMap;
NewMap[5] = "Apple";
NewMap.Remove(3);
// FruitMap == [
//  { Key:4, Value:"Kiwi"   },
//  { Key:5, Value:"Mango"  },
//  { Key:9, Value:"Melon"  },
//  { Key:3, Value:"Orange" }
// ]
// NewMap == [
//  { Key:4, Value:"Kiwi"  },
//  { Key:5, Value:"Apple" },
//  { Key:9, Value:"Melon" }
// ]

同样可以使用 MoveTemp 来移动TMap,移动后,源TMap将为空:

FruitMap = MoveTemp(NewMap);
// FruitMap == [
//  { Key:4, Value:"Kiwi"  },
//  { Key:5, Value:"Apple" },
//  { Key:9, Value:"Melon" }
// ]
// NewMap == []

8、Slack

与TArray相似,Reset 函数所起的作用类似于Empty()调用,但不会释放元素先前使用的内存:

FruitMap.Reset();
// FruitMap == [<invalid>, <invalid>, <invalid>]

但TMap不像TArray一样可以使用 TArray:Max() 检查给TArray分配了多少内存,但仍支持预分配 Slack,Reserve 函数可用于在添加元素前为TMap分配特定数量的Slack:

FruitMap.Reserve(10);
for (int32 i = 0; i < 10; ++i)
{
    FruitMap.Add(i, FString::Printf(TEXT("Fruit%d"), i));
}
// FruitMap == [
//  { Key:9, Value:"Fruit9" },
//  { Key:8, Value:"Fruit8" },
//  ...
//  { Key:1, Value:"Fruit1" },
//  { Key:0, Value:"Fruit0" }
// ]

与TArray一样,我们可以使用 Shrink 来移除末尾没有使用的 Slack,但由于TMap允许其数据结构中存在空位,所以它只会移除末尾的 Slack:

for (int32 i = 0; i < 10; i += 2)
{
    FruitMap.Remove(i);
}
// FruitMap == [
//  { Key:9, Value:"Fruit9" },
//  <invalid>,
//  { Key:7, Value:"Fruit7" },
//  <invalid>,
//  { Key:5, Value:"Fruit5" },
//  <invalid>,
//  { Key:3, Value:"Fruit3" },
//  <invalid>,
//  { Key:1, Value:"Fruit1" },
//  <invalid>
// ]
FruitMap.Shrink();
// FruitMap == [
//  { Key:9, Value:"Fruit9" },
//  <invalid>,
//  { Key:7, Value:"Fruit7" },
//  <invalid>,
//  { Key:5, Value:"Fruit5" },
//  <invalid>,
//  { Key:3, Value:"Fruit3" },
//  <invalid>,
//  { Key:1, Value:"Fruit1" }
// ]

上面的 Shrink函数 只移除了一个处于末尾的空位,但我们可以在调用 Shrink 前调用用 Compact 函数来把所有的空位移到末尾,这样我们就可以移除所有的空位了:

FruitMap.Compact();
// FruitMap == [
//  { Key:9, Value:"Fruit9" },
//  { Key:7, Value:"Fruit7" },
//  { Key:5, Value:"Fruit5" },
//  { Key:3, Value:"Fruit3" },
//  { Key:1, Value:"Fruit1" },
//  <invalid>,
//  <invalid>,
//  <invalid>,
//  <invalid>
// ]
FruitMap.Shrink();
// FruitMap == [
//  { Key:9, Value:"Fruit9" },
//  { Key:7, Value:"Fruit7" },
//  { Key:5, Value:"Fruit5" },
//  { Key:3, Value:"Fruit3" },
//  { Key:1, Value:"Fruit1" }
// ]

9、测试代码

	TMap<int32, FString> FruitMap;
	FruitMap.Add(5, TEXT("Banana"));
	FruitMap.Add(2, TEXT("Grapefruit"));
	FruitMap.Add(7, TEXT("Pineapple"));
	// FruitMap == [
	//  { Key:5, Value:"Banana"     },
	//  { Key:2, Value:"Grapefruit" },
	//  { Key:7, Value:"Pineapple"  }
	// ]

	FruitMap.Add(2, TEXT("Pear"));
	// FruitMap == [
	//  { Key:5, Value:"Banana"    },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" }
	// ]

	FruitMap.Add(4);
	// FruitMap == [
	//  { Key:5, Value:"Banana"    },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:""          }
	// ]

	FruitMap.Emplace(3, TEXT("Orange"));
	// FruitMap == [
	//  { Key:5, Value:"Banana"    },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:""          },
	//  { Key:3, Value:"Orange"    }
	// ]

	TMap<int32, FString> FruitMap2;
	FruitMap2.Emplace(4, TEXT("Kiwi"));
	FruitMap2.Emplace(9, TEXT("Melon"));
	FruitMap2.Emplace(5, TEXT("Mango"));
	FruitMap.Append(FruitMap2);
	// FruitMap == [
	//  { Key:5, Value:"Mango"     },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:"Kiwi"      },
	//  { Key:3, Value:"Orange"    },
	//  { Key:9, Value:"Melon"     }
	// ]

	for (auto& Elem : FruitMap)
	{
		UE_LOG(LogClass, Log, TEXT("Key: %d   Value: %s"), Elem.Key, *Elem.Value);
	}
	// Output:
	//Key: 5   Value : Mango
	//Key: 2   Value : Pear
	//Key: 7   Value : Pineapple
	//Key: 4   Value : Kiwi
	//Key: 3   Value : Orange
	//Key: 9   Value : Melon

	for (auto It = FruitMap.CreateConstIterator(); It; ++It)
	{
		UE_LOG(LogClass, Log, TEXT("Key: %d   Value: %s"), It.Key(), *It.Value());
	}
	// Output:
	//Key: 5   Value : Mango
	//Key: 2   Value : Pear
	//Key: 7   Value : Pineapple
	//Key: 4   Value : Kiwi
	//Key: 3   Value : Orange
	//Key: 9   Value : Melon

	int32 Count1 = FruitMap.Num();
	// Count1 == 6

	FString Val7 = FruitMap[7];
	// Val7 == "Pineapple"
	//FString Val8 = FruitMap[8]; // assert!

	bool bHas7 = FruitMap.Contains(7);
	bool bHas8 = FruitMap.Contains(8);
	// bHas7 == true
	// bHas8 == false

	FString* Ptr7 = FruitMap.Find(7);
	FString* Ptr8 = FruitMap.Find(8);
	// *Ptr7 == "Pineapple"
	//  Ptr8 == nullptr

	FString& Ref7 = FruitMap.FindOrAdd(7);
	// Ref7     == "Pineapple"
	// FruitMap == [
	//  { Key:5, Value:"Mango"     },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:"Kiwi"      },
	//  { Key:3, Value:"Orange"    },
	//  { Key:9, Value:"Melon"     }
	// ]
	FString& Ref8 = FruitMap.FindOrAdd(8);
	// Ref8     == ""
	// FruitMap == [
	//  { Key:5, Value:"Mango"     },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:"Kiwi"      },
	//  { Key:3, Value:"Orange"    },
	//  { Key:9, Value:"Melon"     },
	//  { Key:8, Value:""          }
	// ]

	FString Val71 = FruitMap.FindRef(7);
	FString Val6 = FruitMap.FindRef(6);
	// Val71     == "Pineapple"
	// Val6     == ""
	// FruitMap == [
	//  { Key:5, Value:"Mango"     },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:"Kiwi"      },
	//  { Key:3, Value:"Orange"    },
	//  { Key:9, Value:"Melon"     },
	//  { Key:8, Value:""          }
	// ]

	const int32* KeyMangoPtr = FruitMap.FindKey(TEXT("Mango"));
	const int32* KeyKumquatPtr = FruitMap.FindKey(TEXT("Kumquat"));
	// *KeyMangoPtr   == 5
	//  KeyKumquatPtr == nullptr

	TArray<int32>   FruitKeys;
	TArray<FString> FruitValues;
	FruitKeys.Add(999);
	FruitKeys.Add(123);
	FruitMap.GenerateKeyArray(FruitKeys);
	FruitMap.GenerateValueArray(FruitValues);
	// FruitKeys   == [ 5,2,7,4,3,9,8 ]
	// FruitValues == [ "Mango","Pear","Pineapple","Kiwi","Orange","Melon","" ]

	FruitMap.Remove(8);
	// FruitMap == [
	//  { Key:5, Value:"Mango"     },
	//  { Key:2, Value:"Pear"      },
	//  { Key:7, Value:"Pineapple" },
	//  { Key:4, Value:"Kiwi"      },
	//  { Key:3, Value:"Orange"    },
	//  { Key:9, Value:"Melon"     }
	// ]


	FString Removed7 = FruitMap.FindAndRemoveChecked(7);
	// Removed7 == "Pineapple"
	// FruitMap == [
	//  { Key:5, Value:"Mango"  },
	//  { Key:2, Value:"Pear"   },
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:3, Value:"Orange" },
	//  { Key:9, Value:"Melon"  }
	// ]

	//FString Removed8 = FruitMap.FindAndRemoveChecked(8); // assert!

	FString Removed;
	bool bFound2 = FruitMap.RemoveAndCopyValue(2, Removed);
	// bFound2  == true
	// Removed  == "Pear"
	// FruitMap == [
	//  { Key:5, Value:"Mango"  },
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:3, Value:"Orange" },
	//  { Key:9, Value:"Melon"  }
	// ]
	bool bFound8 = FruitMap.RemoveAndCopyValue(8, Removed);
	// bFound8  == false
	// Removed  == "Pear", i.e. unchanged
	// FruitMap == [
	//  { Key:5, Value:"Mango"  },
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:3, Value:"Orange" },
	//  { Key:9, Value:"Melon"  }
	// ]


	FruitMap.KeySort([](int32 A, int32 B) {
		return A > B; // sort keys in reverse
		});
	// FruitMap == [
	//  { Key:9, Value:"Melon"  },
	//  { Key:5, Value:"Mango"  },
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:3, Value:"Orange" }
	// ]

	FruitMap.ValueSort([](const FString& A, const FString& B) {
		return A.Len() < B.Len(); // sort strings by length
		});
	// FruitMap == [
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:5, Value:"Mango"  },
	//  { Key:9, Value:"Melon"  },
	//  { Key:3, Value:"Orange" }
	// ]


	TMap<int32, FString> NewMap = FruitMap;
	NewMap[5] = "Apple";
	NewMap.Remove(3);
	// FruitMap == [
	//  { Key:4, Value:"Kiwi"   },
	//  { Key:5, Value:"Mango"  },
	//  { Key:9, Value:"Melon"  },
	//  { Key:3, Value:"Orange" }
	// ]
	// NewMap == [
	//  { Key:4, Value:"Kiwi"  },
	//  { Key:5, Value:"Apple" },
	//  { Key:9, Value:"Melon" }
	// ]


	FruitMap = MoveTemp(NewMap);
	// FruitMap == [
	//  { Key:4, Value:"Kiwi"  },
	//  { Key:5, Value:"Apple" },
	//  { Key:9, Value:"Melon" }
	// ]
	// NewMap == []


	FruitMap.Reset();
	// FruitMap == [<invalid>, <invalid>, <invalid>]

	FruitMap.Reserve(10);
	for (int32 i = 0; i < 10; ++i)
	{
		FruitMap.Add(i, FString::Printf(TEXT("Fruit%d"), i));
	}
	// FruitMap == [
	//  { Key:9, Value:"Fruit9" },
	//  { Key:8, Value:"Fruit8" },
	//  ...
	//  { Key:1, Value:"Fruit1" },
	//  { Key:0, Value:"Fruit0" }
	// ]

	for (int32 i = 0; i < 10; i += 2)
	{
		FruitMap.Remove(i);
	}
	// FruitMap == [
	//  { Key:9, Value:"Fruit9" },
	//  <invalid>,
	//  { Key:7, Value:"Fruit7" },
	//  <invalid>,
	//  { Key:5, Value:"Fruit5" },
	//  <invalid>,
	//  { Key:3, Value:"Fruit3" },
	//  <invalid>,
	//  { Key:1, Value:"Fruit1" },
	//  <invalid>
	// ]
	FruitMap.Shrink();
	// FruitMap == [
	//  { Key:9, Value:"Fruit9" },
	//  <invalid>,
	//  { Key:7, Value:"Fruit7" },
	//  <invalid>,
	//  { Key:5, Value:"Fruit5" },
	//  <invalid>,
	//  { Key:3, Value:"Fruit3" },
	//  <invalid>,
	//  { Key:1, Value:"Fruit1" }
	// ]

	FruitMap.Compact();
	// FruitMap == [
	//  { Key:9, Value:"Fruit9" },
	//  { Key:7, Value:"Fruit7" },
	//  { Key:5, Value:"Fruit5" },
	//  { Key:3, Value:"Fruit3" },
	//  { Key:1, Value:"Fruit1" },
	//  <invalid>,
	//  <invalid>,
	//  <invalid>,
	//  <invalid>
	// ]
	FruitMap.Shrink();
	// FruitMap == [
	//  { Key:9, Value:"Fruit9" },
	//  { Key:7, Value:"Fruit7" },
	//  { Key:5, Value:"Fruit5" },
	//  { Key:3, Value:"Fruit3" },
	//  { Key:1, Value:"Fruit1" }
	// ]