jjzjj

java - 结合字母顺序和自然顺序(又名。用户理智排序)

coder 2024-03-28 原文

我认为这很容易找到预制的,但似乎我在网上找到的任何解决方案都只能解决部分问题。

我想对用户提供的文件名列表进行排序(这些文件大多以人和/或地址命名),有时使用不同的语言(主要是德语,带有一些法语和意大利语在这里和那里混合,很少有任何其他西方语言)。

这个想法是以(德国)用户通常认为理智的方式呈现这个列表。这意味着顺序应遵循 locale.GERMAN 的 java.text.Collat​​or,但同时期望对字符串中的数字进行异常(exception)处理,因此“10”出现在“2”之后".

我找到了在网络上进行自然排序的代码,但它依赖于逐字符比较(而 Collat​​or 不支持)。我可以用子字符串破解一些东西,但在比较器内部,我认为在每个比较调用上创建多个子字符串并不是最明智的想法。

有什么想法可以有效地实现这一点(在执行时间和实现 时间),或者更好的是一个经过测试和随时可用的实现?

最佳答案

这是已接受答案中的改编代码(基于 The Alphanum Algorithm )。代码经过优化以减少垃圾产生并处理前导零 (01 < 001="">< 2)。它还被通用化,现在更加灵活,因为它不再局限于="" java.lang.string,而是现在采用="">

import java.text.Collator;
import java.util.Comparator;

/**
 * Comparator for ordering by Collator while treating digits numerically.
 * This provides a "natural" order that humans usually perceive as 'logical'.
 * 
 * It should work reasonably well for western languages (provided you
 * use the proper collator when constructing). For free control over the
 * Collator, use the constructor that takes a Collator as parameter.
 * Configure the Collator using Collator.setDecomposition()/setStrength()
 * to suit your requirements.
 */
public class AlphanumComparator implements Comparator<CharSequence> {

    /**
     * The collator used for comparison of the alpha part
     */
    private final Collator collator;

    /**
     * Create comparator using platform default collator.
     * (equivalent to using Collator.getInstance())
     */
    public AlphanumComparator() {
        this(Collator.getInstance()); 
    }

    /**
     * Create comparator using specified collator
     */
    public AlphanumComparator(final Collator collator) {
        if (collator == null)
            throw new IllegalArgumentException("collator must not be null");
        this.collator = collator;
    }

    /**
     * Ideally this would be generalized to Character.isDigit(), but I have
     * no knowledge about arabic language and other digits, so I treat
     * them as characters...
     */
    private static boolean isDigit(final int character) {
        // code between ASCII '0' and '9'?
        return character >= 48 && character <= 57;
    }

    /**
     * Get subsequence of only characters or only digits, but not mixed
     */
    private static CharSequence getChunk(final CharSequence charSeq, final int start) {
        int index = start;
        final int length = charSeq.length();
        final boolean mode = isDigit(charSeq.charAt(index++));
        while (index < length) {
            if (isDigit(charSeq.charAt(index)) != mode)
                break;
            ++index;
        }
        return charSeq.subSequence(start, index);
    }

    /**
     * Implements Comparator<CharSequence>.compare
     */
    public int compare(final CharSequence charSeq1, final CharSequence charSeq2) {
        final int length1 = charSeq1.length();
        final int length2 = charSeq2.length();
        int index1 = 0;
        int index2 = 0;
        int result = 0;
        while (result == 0 && index1 < length1 && index2 < length2) {
            final CharSequence chunk1 = getChunk(charSeq1, index1);
            index1 += chunk1.length();

            final CharSequence chunk2 = getChunk(charSeq2, index2);
            index2 += chunk2.length();

            if (isDigit(chunk1.charAt(0)) && isDigit(chunk2.charAt(0))) {
                final int clen1 = chunk1.length();
                final int clen2 = chunk2.length();
                // count and skip leading zeros
                int zeros1 = 0;
                while (zeros1 < clen1 && chunk1.charAt(zeros1) == '0')
                    ++zeros1;
                // count and skip leading zeros
                int zeros2 = 0;
                while (zeros2 < clen2 && chunk2.charAt(zeros2) == '0')
                    ++zeros2;
                // the longer run of non-zero digits is greater
                result = (clen1 - zeros1) - (clen2 - zeros2);
                // if the length is the same, the first differing digit decides
                // which one is deemed greater.
                int subi1 = zeros1;
                int subi2 = zeros2;
                while (result == 0 && subi1 < clen1 && subi2 < clen2) {
                    result = chunk1.charAt(subi1++) - chunk2.charAt(subi2++);
                }
                // if still no difference, the longer zeros-prefix is greater
                if (result == 0)
                    result = subi1 - subi2;
            } else {
                // in case we are working with Strings, toString() doesn't create
                // any objects (String.toString() returns the same string itself).
                result = collator.compare(chunk1.toString(), chunk2.toString());
            }
        }
        // if there was no difference at all, let the longer one be the greater one
        if (result == 0)
            result = length1 - length2;
        // limit result to (-1, 0, or 1)
        return Integer.signum(result);
    }


}

编辑 2014-12-01:Konstantin Petrukhnov 在评论中指出的固定版本。

关于java - 结合字母顺序和自然顺序(又名。用户理智排序),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12640280/

有关java - 结合字母顺序和自然顺序(又名。用户理智排序)的更多相关文章

  1. ruby-on-rails - 结合 meta_search 与 acts_as_taggable_on - 2

    我在开发的Rails3网站的一些搜索功能上遇到了一个小问题。我有一个简单的Post模型,如下所示:classPost我正在使用acts_as_taggable_on来更轻松地向我的帖子添加标签。当我有一个标记为“rails”的帖子并执行以下操作时,一切正常:@posts=Post.tagged_with("rails")问题是,我还想搜索帖子的标题。当我有一篇标题为“Helloworld”并标记为“rails”的帖子时,我希望能够通过搜索“hello”或“rails”来找到这篇帖子。因此,我希望标题列的LIKE语句与acts_as_taggable_on提供的tagged_with方法

  2. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  3. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  4. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  5. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  6. ruby - 匹配大写字母并用后续字母填充,直到一定的字符串长度 - 2

    我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种

  7. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  8. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  9. ruby - RVM "ERROR: Unable to checkout branch ."单用户 - 2

    我在新的Debian6VirtualBoxVM上安装RVM时遇到问题。我已经安装了所有需要的包并使用下载了安装脚本(curl-shttps://rvm.beginrescueend.com/install/rvm)>rvm,但以单个用户身份运行时bashrvm我收到以下错误消息:ERROR:Unabletocheckoutbranch.安装在这里停止,并且(据我所知)没有安装RVM的任何文件。如果我以root身份运行脚本(对于多用户安装),我会收到另一条消息:Successfullycheckedoutbranch''安装程序继续并指示成功,但未添加.rvm目录,甚至在修改我的.bas

  10. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

随机推荐