std::map с ключом Vector3 ПРОТИВ std::vector с использованием составного индекса

В данный момент я создаю карту тайлов и пытаюсь решить, как хранить и ссылаться на каждый тайл. Мои 2 текущих варианта находятся между:

  1. std::vector, где в качестве ключа я использую составной x, y, z calc;
  2. Или std::map с использованием Vector3i в качестве ключа.

Мой вопрос: к какому методу я должен склоняться/предпочитать, исходя из простоты использования и/или производительности. Я предполагаю, что память/размер/хранилище между различными методами также могут принять решение. Критика приветствуется, так как я хотел бы услышать некоторые мнения, прежде чем основывать свой движок на этом.

Вот пример использования составного вектора:

#include <vector>
#include <algorithm>
#include <iostream>

unsigned int mapWidth = 10, mapHeight = 10, mapDepth = 2;

struct Vector3i {
    Vector3i() {};
    Vector3i(unsigned int x, unsigned int y, unsigned int z) : x(x), y(y), z(z) {}
    unsigned int x, y, z;
};

struct Tile {
    Tile() {};
    std::vector<unsigned int> children;
    bool visible;
    Vector3i coord;
};

unsigned int getIndex(unsigned int x, unsigned int y, unsigned int z) {
    return (y*mapWidth) + x + (z * (mapWidth * mapHeight));
}

int main() {

    std::vector<Tile> tiles;
    tiles.resize(mapWidth * mapHeight * mapDepth);

    for( int x = 0; x < mapWidth; x++ ) {
        for( int y = 0; y < mapHeight; y++ ) {
            for( int z = 0; z < mapDepth; z++) {
                unsigned int idx = getIndex(x,y,z);
                tiles[idx].coord = Vector3i(x,y,z);
                tiles[idx].visible = true;
                tiles[idx].children.push_back(1);
            }
        }
    }

    std::for_each(tiles.begin(), tiles.end(), [&] (const Tile& tile) {
        std::cout << tile.coord.x << "," << tile.coord.y << "," << tile.coord.z << std::endl;
    });

    const Tile& myTile = tiles[getIndex(1,1,1)];
    std::cout << '\n' << myTile.coord.x << "," << myTile.coord.y << "," << myTile.coord.z << std::endl;

    return 0;
};

А вот пример использования метода std::map:

#include <vector>
#include <map>
#include <iostream>
#include <algorithm>
#include <functional>

struct Vector3i {
    Vector3i() {};
    Vector3i(unsigned int x, unsigned int y, unsigned int z) : x(x), y(y), z(z) {}
    unsigned int x, y, z;
};

struct Tile {
    std::vector<unsigned int> children;
    bool visible;
    Vector3i coord;
};

class comparator {
    public:
        bool operator()(const Vector3i& lhs, const Vector3i& rhs) const {
            return lhs.x < rhs.x
                || ( lhs.x == rhs.x && ( lhs.y < rhs.y
                || ( lhs.y == rhs.y && lhs.z < rhs.z)));
        }
};

int main() {
    unsigned int mapWidth = 5, mapHeight = 5, mapDepth = 2;
    std::map<Vector3i, Tile, comparator> tiles;

    for( int x = 0; x < mapWidth; x++ ) {
        for( int y = 0; y < mapHeight; y++ ) {
            for( int z = 0; z < mapDepth; z++) {
                Vector3i key(z,y,x);

                Tile tile;
                tile.coord = Vector3i(x,y,z);
                tile.visible = true;
                tile.children.push_back(1);

                tiles.insert(std::make_pair<Vector3i, Tile>(key, tile));
            }
        }
    }

    for( std::map<Vector3i, Tile, comparator>::iterator iter = tiles.begin(); iter != tiles.end(); ++iter ) {
        std::cout << iter->second.coord.x << "," << iter->second.coord.y << "," << iter->second.coord.z << std::endl;
    }

    auto found = tiles.find(Vector3i(1,1,1));
    const Tile& myTile = found->second;
    std::cout << '\n' << myTile.coord.x << "," << myTile.coord.y << "," << myTile.coord.z << std::endl;

    return 0;
};

Хорошо, большое спасибо!


person DanoThom    schedule 17.09.2013    source источник
comment
Я думаю, что все в порядке из-за вашего дизайна. Я предпочитаю использовать вектор, если дырки нет, и карту, если дырки есть.   -  person BigTailWolf    schedule 17.09.2013


Ответы (1)


Использование одного вектора для хранения всей вашей тайловой карты потребует, чтобы вся тайловая карта находилась в непрерывном блоке памяти. В зависимости от размера тайловой карты это может быть проблемой, а может и не быть (с вашими текущими размерами ее не будет, но если бы она когда-либо была расширена до большего 3D-пространства, она могла бы легко стать слишком большой, чтобы попытаться выделить в одном непрерывном блоке).

Если вам нужно перебирать трехмерное пространство каким-то упорядоченным образом, карта не будет вашим лучшим контейнером (поскольку элементы не обязательно будут находиться в том логическом порядке, который вам нужен для целей отображения).

Другим потенциальным решением является использование вектора векторов векторов для тайлов, что позволит упростить итерацию по вашему 3D-пространству, а также не потребует массивного непрерывного блока данных (только одномерный ряд тайлов должен быть непрерывным).

person Zac Howland    schedule 17.09.2013
comment
Я ценю понимание, в значительной степени тот ответ, который я искал. Я еще не думал о будущем или расширяемости в отношении ограничений памяти, но буду иметь в виду ваш комментарий. Спасибо. - person DanoThom; 18.09.2013