【问题标题】:Should I use a HashMap, List, or something else?我应该使用 HashMap、List 还是其他东西?
【发布时间】:2017-05-22 06:36:12
【问题描述】:

我正在编写一个程序

  1. 存储键值对列表(场景,listOfResults)
  2. 随机模拟一个新场景,找到结果
  3. 检查场景是否在键值对列表中
  4. 如果是,则更新该键值对,以便将新结果添加到列表中
  5. 否则,将场景-结果对添加到键值对列表中
  6. 重复步骤 #2-5

目前我正在使用 HashMap,因为它的键值系统是有意义的。但是,计算似乎需要很长时间,我正在考虑不同的数据结构是否更合适。

模拟 100 个场景用时 8.623 秒,并留下 HashMap 4,600 个键值对。

模拟 200 个场景耗时 42.690 秒,并留下 HashMap 9,431 个键值对。

看起来键值对的数量呈线性增长,而时间呈指数增长,很快就会失控。我也许可以进一步优化程序,但我是否完全使用了错误的数据结构?

更新:我怀疑问题出在我的 hashcode() 方法上。这里是:

@Override
public int hashCode() {
    int result = 31 + legalBoard;
    result = result*31 + playerToMove;
    result = result*31 + Arrays.hashCode(getSmallestFieldConfiguration());
    //System.out.println("Hashcode: " + result + " ---------- " + Arrays.toString(field));
    return result;
}

legalBoard 是介于 -1 和 8 之间的 int。playerToMove 是 -1 或 1。field 是具有值 -1、0 和 1 的 int[81]。getSmallestConfiguration() 方法发现阵列的所有可能反射/旋转中的最小阵列,如下所示:

public int[] getSmallestFieldConfiguration(){       
    int[] smallestConfig = field;
    for(int[] config : getAllOtherFieldConfigurations()){
        if(isGreater(smallestConfig, config)){
            smallestConfig = config;
        }
    }
    return smallestConfig;
}

public int[][] getAllOtherFieldConfigurations(){
    int[][] configs = new int[7][];
    int[][] twoDimensionalField = new int[][]{
        {field[0],field[1],field[2],field[3],field[4],field[5],field[6],field[7],field[8]},
        {field[9],field[10],field[11],field[12],field[13],field[14],field[15],field[16],field[17]},
        {field[18],field[19],field[20],field[21],field[22],field[23],field[24],field[25],field[26]},
        {field[27],field[28],field[29],field[30],field[31],field[32],field[33],field[34],field[35]},
        {field[36],field[37],field[38],field[39],field[40],field[41],field[42],field[43],field[44]},
        {field[45],field[46],field[47],field[48],field[49],field[50],field[51],field[52],field[53]},
        {field[54],field[55],field[56],field[57],field[58],field[59],field[60],field[61],field[62]},
        {field[63],field[64],field[65],field[66],field[67],field[68],field[69],field[70],field[71]},
        {field[72],field[73],field[74],field[75],field[76],field[77],field[78],field[79],field[80]},
    };
    /*for(int i=0; i<81; i++){
        twoDimensionalField[i%9][i/9] = field[i];
    }*/

    //Reflections
    configs[0] = getFieldFromMatrix(MatrixTransformations.reflectVertical(twoDimensionalField));
    configs[1] = getFieldFromMatrix(MatrixTransformations.reflectHorizontal(twoDimensionalField));
    //Rotations
    int[][] singleRotation = MatrixTransformations.rotate(twoDimensionalField);
    configs[2] = getFieldFromMatrix(singleRotation);
    int[][] doubleRotation = MatrixTransformations.rotate(twoDimensionalField);
    configs[3] = getFieldFromMatrix(doubleRotation);
    configs[4] = getFieldFromMatrix(MatrixTransformations.rotate(doubleRotation));
    //Transpositions
    configs[5] = getFieldFromMatrix(MatrixTransformations.transpose(twoDimensionalField));
    configs[6] = getFieldFromMatrix(MatrixTransformations.transpose(doubleRotation));

    return configs;
}

MatrixTransformations 方法如下所示:

公共类 MatrixTransformations { 公共 MatrixTransformations(){}

public static int[][] reflectVertical(int[][] arr){
    for(int j=0; j<9; j++){
        for(int k=0; k<4; k++){
            int temp = arr[j][k];
            arr[j][k] = arr[j][3-k];
            arr[j][8-k] = temp;
        }
    }
    return arr;
}
public static int[][] reflectHorizontal(int[][] arr){
    for(int j=0; j<4; j++){
        for(int k=0; k<9; k++){
            int temp = arr[j][k];
            arr[j][k] = arr[8-j][k];
            arr[8-j][k] = temp;
        }
    }
    return arr;
}
public static int[][] transpose (int[][] array) {
      if (array == null || array.length == 0)//empty or unset array, nothing do to here
        return array;

      int width = array.length;
      int height = array[0].length;

      int[][] array_new = new int[height][width];

      for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
          array_new[y][x] = array[x][y];
        }
      }
      return array_new;
    }

public static int[][] rotate(int[][] arr){
    int[][] newArr = new int[9][9];
    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            newArr[i][j] = arr[8 - j][i];
        }
    }
    return newArr;
}
}

【问题讨论】:

  • 我们怎么知道?你没有发布任何代码。代码很重要。
  • 您应该发布包含较长执行时间的代码。问题可能不是地图,而是它的使用方式或代码中的其他内容。
  • 同意 davidxxx - 根据(very 模糊!)的描述,一个疯狂的猜测是第 4 步很昂贵。 “更新”列表听起来可能很昂贵,如果它涉及list.contains(something) 之类的东西。其他操作应该是 O(1)(至少,你列出的那些,如果实施得当)
  • @Marco13 这也是我的怀疑。有什么快速的方法来“更新”一个键的值吗?我现在的方法似乎很慢;它类似于 map.put(key, map.get(key).getUpdatedValue(myResult));
  • 不贴getUpdatedValue的代码大家只能猜了。

标签: java list arraylist linked-list hashmap


【解决方案1】:

也许你应该坚持使用地图。除非您的密钥是连续的 1,2,3 使用 9K 的随机数可能会让您陷入内存不足的问题。

【讨论】:

  • 谢谢。我怀疑我列表中的第 4 步(替换功能)是最耗时的。有没有更快的方法来“更新”给定键的值,而不需要像 map.put(key, map.get(key).getUpdatedKey(thingIHaveToAdd)) 这样的东西?
  • 如前所述,更多的代码会更好,但您的瓶颈已基本确定,您不必花费太多时间来浏览可用的结构并衡量什么可以最大程度地减少痛苦。
  • 在任何事情之前,我都会使用分析器:VisualVM 等。最好从硬数字开始。这将告诉您流血的位置。可能是调用 hashCode(...) 后的特定部分
猜你喜欢
  • 1970-01-01
  • 2014-02-10
  • 2011-06-11
  • 1970-01-01
  • 2011-11-04
  • 1970-01-01
  • 2011-04-03
  • 2011-02-02
  • 1970-01-01
相关资源
最近更新 更多