在 Java 中存储 Enum 的顺序

2024-04-19

在java中,EnumSet使用位掩码/位向量存储它包含的项目long (RegularEnumSet) or long[] (JumboEnumSet)。我现在遇到了一个用例,其中我有数千个域对象(让我们称它们为Node),每个都将显示枚举的所有项目(我们称之为Flag)的顺序因对象而异。

目前我将订单存储为番石榴ImmutableSet https://google.github.io/guava/releases/23.0/api/docs/com/google/common/collect/ImmutableSet.html,因为这保证保留插入顺序。不过,我已经用过本页解释的方法 http://javaspecialists.co.za/archive/Issue029.html比较内存使用情况EnumSet<Flag>, an ImmutableSet<Flag> and a Flag[]。以下是当 a) Flag 有 64 个枚举项且 b) 所有三个变体都包含全部 64 个项时的结果:

枚举集:32 字节
不可变集:832 字节
数组:272 字节

所以我的问题是:是否有一种聪明的方法将枚举排序打包为数值以获得比数组更小的内存占用?如果它有所不同:在我的用例中,我会假设排序始终包含所有枚举项。

澄清一下:我的枚举比这小得多,而且到目前为止我没有任何内存问题,这种情况也不太可能给我带来内存问题。只是这种低效率让我烦恼,即使是在微观层面上也是如此。

Update:

根据各种答案和评论的建议,我想出了这个使用字节数组的数据结构。警告:它不实现 Set 接口(不检查唯一值),并且不会扩展到超出字节可以容纳的大型枚举。另外,复杂性非常糟糕,因为 Enum.values() 必须重复查询(有关此问题的讨论请参见此处 http://code.google.com/p/guava-libraries/issues/detail?id=1022),但这里是:

public class EnumOrdering<E extends Enum<E>> implements Iterable<E> {
    private final Class<E> type;
    private final byte[] order;

    public EnumOrdering(final Class<E> type, final Collection<E> order) {
        this.type = type;

        this.order = new byte[order.size()];

        int offset = 0;
        for (final E item : order) {
            this.order[offset++] = (byte) item.ordinal();
        }

    }

    @Override
    public Iterator<E> iterator() {
        return new AbstractIterator<E>() {
            private int offset = -1;
            private final E[] enumConstants = type.getEnumConstants();

            @Override
            protected E computeNext() {
                if (offset < order.length - 1) {
                    return enumConstants[order[++offset]];
                }
                return endOfData();
            }
        };
    }
}

内存占用为:

枚举排序:104

到目前为止,这是一个相当不错的结果,感谢 bestsss 和 JB Nizet!

更新:我已将代码更改为仅实现 Iterable,因为其他任何内容都需要 equals / hashCode / contains 等的合理实现。


有没有一种巧妙的方法将枚举排序打包为数值

是的,您可以将排序表示为数值,但要使用它,您需要转换回 byte/int 数组。因为有 64 个! 64 个值的可能排序,以及 64!大于Long.MAX_VALUE,您需要将数字存储在BigInteger。我想这将是存储排序的最节省内存的方式,尽管您在内存中获得的内容会由于必须将数字转换为数组而失去时间。

有关在数字/数组表示之间进行转换的算法,请参阅这个问题 https://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms.

这是上述方法的另一种选择,不知道它是否与那个方法一样有效,并且您必须将代码从int to BigInteger基于,但它应该足以给你这个想法:

/**
   * Returns ith permutation of the n numbers [from, ..., to]
   * (Note that n == to - from + 1).
   * permutations are numbered from 0 to n!-1, if i is outside this
   * range it is treated as i%n! 
   * @param i
   * @param from
   * @param n
   * @return
   */
  public static int[] perm(long i, int from, int to)
  {
    // method specification numbers permutations from 0 to n!-1.
    // If you wanted them numbered from 1 to n!, uncomment this line.
    //  i -= 1;
    int n = to - from + 1;

    int[] initArr  = new int[n];             // numbers [from, ..., to]
    int[] finalArr = new int[n];             // permutation of numbers [from, ..., to]

    // populate initial array
    for (int k=0; k<n; k++)
      initArr[k] = k+from;

    // compute return array, element by element
    for (int k=0; k<n; k++) {
      int index = (int) ((i%factorial(n-k)) / factorial(n-k-1));

      // find the index_th element from the initial array, and
      // "remove" it by setting its value to -1
      int m = convertIndex(initArr, index);
      finalArr[k] = initArr[m];
      initArr[m] = -1;
    }

    return finalArr;
  }


  /** 
   * Helper method used by perm.
   * Find the index of the index_th element of arr, when values equal to -1 are skipped.
   * e.g. if arr = [20, 18, -1, 19], then convertIndex(arr, 2) returns 3.
   */
  private static int convertIndex(int[] arr, int index)
  {
    int m=-1;
    while (index>=0) {
      m++;
      if (arr[m] != -1)
        index--;
    }

    return m;
  }

基本上,您按照自然顺序从初始化数组开始,然后循环最终数组,每次计算接下来应该放置哪些剩余元素。此版本通过将值设置为 -1 从 init 数组中“删除”元素。使用 a 可能会更直观List or LinkedList,我刚刚从我手边的一些旧代码中粘贴了这个。

使用上述方法并以此作为main:

public static void main(String[] args) {
    int n = (int) factorial(4);
    for ( int i = 0; i < n; i++ ) {
      System.out.format( "%d: %s\n", i, Arrays.toString( perm(i, 1, 4 ) ) );
    }
}

您将得到以下输出:

0: [1, 2, 3, 4]
1: [1, 2, 4, 3]
2: [1, 3, 2, 4]
3: [1, 3, 4, 2]
4: [1, 4, 2, 3]
5: [1, 4, 3, 2]
6: [2, 1, 3, 4]
7: [2, 1, 4, 3]
8: [2, 3, 1, 4]
9: [2, 3, 4, 1]
10: [2, 4, 1, 3]
11: [2, 4, 3, 1]
12: [3, 1, 2, 4]
13: [3, 1, 4, 2]
14: [3, 2, 1, 4]
15: [3, 2, 4, 1]
16: [3, 4, 1, 2]
17: [3, 4, 2, 1]
18: [4, 1, 2, 3]
19: [4, 1, 3, 2]
20: [4, 2, 1, 3]
21: [4, 2, 3, 1]
22: [4, 3, 1, 2]
23: [4, 3, 2, 1]

这是 ideone 上的可执行版本 http://ideone.com/bq35C.

判断依据BigInteger.bitLength(),应该可以在不超过 37 个字节中存储 64 个元素的排序(加上使用BigInteger实例)。我不知道这是否值得,但这是一个很好的练习!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Java 中存储 Enum 的顺序 的相关文章

随机推荐

  • Python 3 中的 FastCGI WSGI 库?

    对于 Python 3 是否存在可以将 WSGI 应用程序用作 FastCGI 服务器的库 这样 nginx 就可以将请求代理给它了 Python 3 文档 https docs python org 3 howto webservers
  • 重定向子进程标准输出

    我已经使用 test py 中的 Redir 类设置了标准输出重定向 如下 输出应在文本框中显示两个打印语句 但目前只有 Output1 被发送到文本框 Output2 在后面的控制台中打印 我想知道是否有办法重定向子进程的标准输出 我尝试
  • 启动套接字服务器会干扰 gRPC/http 客户端服务器通信 Golang

    我有一个很大程度上基于此的服务器tutorial https www golinuxcloud com go grpc crud api postgresql db 在我对其应用了额外的更改后 它工作得很好 但现在我想添加socket io
  • Java“重复局部变量” - 是在 Java 还是 Eclipse 中抛出错误?

    在下面的代码中 private static void example String inputString test switch inputString case test String outputString The string
  • 如何访问 Pyramid .ini 文件中的自定义部分?

    我目前正在为多种服务编写数据收集服务 可能有 5 个不同的 API 端点 具有不同的主机和端口号 我想为此创建一个设置文件 但认为 ini应该是一个更好的地方或者我是这么想的 我的development ini 看起来像这样 app mai
  • 如何让 Symfony 2 采用协议方案(http vs https)

    我有一个 Symfony 2 网站 在开发中运行在 HTTP 上 在生产中运行在 HTTPS 上 我注意到在生产中 Symfony 生成的 URL 仍然全部呈现为 HTTP 我怎么也可以 让框架采用当前提供网站的协议 可能是首选 或者 仅在
  • 如何检查视频文件是否大于2MB?

    假设我从 iPhone 库中获取了一个视频文件 我想检查视频文件不应大于 2MB 我无法使用 videoMaximumDuration 方法 因为如果任何视频是高清质量的 即使是 1 分钟持续时间的视频也可能会很大 有什么意见吗 urlvi
  • 为整个服务器/域强制使用 https

    我正在开发一些只能通过 https 访问的表单 我有一个专用服务器 有自己的证书和所有好东西 所以我的问题实际上有两个 1 强制每个请求都为 https 的最佳方法是什么 有没有比这个 htacess mod rewrite 规则更好的方法
  • Gmaps.js 停止工作

    从一天到另一天 Gmaps js 库停止工作 我创建了一张带有 3 个标记的地图 他们有 InfoWindows 我在 InfoWindow 中添加了一个小路由选项 您可以在其中输入您的地址中 Gmaps 将您引导至该点 现在 在过去两周内
  • __non_webpack_require__ 未定义

    我是 webpack 和 node 的新手 我想知道如何使用 non webpack require 功能 我去过webpack 的网站 https webpack js org api module variables non webpa
  • Azure 函数 python 命名参数没有值

    我目前正在 azure 函数中使用 python 创建一个计时器触发器 该触发器聚合来自 blob 存储的数据 并将结果放入 cosmosDB 中 我的问题如下 当我在绑定路径中使用特定文件时 函数按预期运行 每当我更改它 以便获取容器中的
  • 如何在 PHP 5.3+ 中的命名空间类内部使用全局命名空间类型提示?

    namespace MyClass Util class Sample public function each Object f 来自调用文件 未命名空间 sample new Sample sample gt each new stdC
  • 如何使用 mechanize 获取生成的验证码图像

    我正在尝试使用 python 和 mechanize 从我的移动提供商网站发送短信 问题是表单有验证码图像 使用 mechanize 我可以获取图像的链接 但每次访问该链接时它都是不同的 有什么办法可以从 mechanize 获得准确的图片
  • 执行某些命令(如“prune”)时,Wincred 无法与 Git Bash(Git for Windows)正常工作

    我已经在 Windows 7 64 位中很好地设置了 GitforWindows 并将凭据管理器设置为 Wincred 然而 当我运行一些命令时 比如git remote prune origin在 GitBash 中 尽管运行命令 但它在
  • 在 Power BI 自定义视觉对象中使用 d3.js 库绘制一条线

    我正在努力在 Power BI 自定义视觉对象中绘制一条单线 Power BI 中的报表是使用 TypeScript 和 d3 js v 3 0 编写的 我可以用轴绘制图表 但没有出现线条 在 HTML 文件中使用纯 d3 js 确实很容易
  • wxHTTP 和线程

    我在线程内使用 wxHTTP 时遇到一些问题 我创建了以下从 wxThread 派生的类来使用 wxHTTP class Thread public wxThread private wxHTTP get public Thread Thr
  • 在哪里设置服务引用上的 CookieContainer?

    例如 当将 WebService 引用添加到 NET 2 0 项目上的 ASMX 服务时 var objService new NameSpace groupservices 那里存在 objService CookieContainer
  • 不在办公室时进行源代码控制

    有时我不在办公室时会编写代码 我想在未连接到公司网络时继续使用源代码控制的好处 我理想的系统将允许我将修订签入笔记本电脑上的存储库 然后当我连接到公司网络时 该存储库将与我们的主 SVN 存储库同步 当与主存储库同步时 如果可以维护单独的签
  • 这是企图破坏我的 ASP.Net 站点的安全吗?

    我对 ASP NET 还很陌生 我最近在我的网站上设置了自动电子邮件 以通知我未处理的异常情况 就在几个小时前 3 分钟内出现了 10 个未处理的异常 并且所有堆栈跟踪都是相似的 错误消息中有很多我不明白的内容 但我不喜欢它的样子 以下是其
  • 在 Java 中存储 Enum 的顺序

    在java中 EnumSet使用位掩码 位向量存储它包含的项目long RegularEnumSet or long JumboEnumSet 我现在遇到了一个用例 其中我有数千个域对象 让我们称它们为Node 每个都将显示枚举的所有项目