Java 如何对字母数字字符串进行排序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27169234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:55:41  来源:igfitidea点击:

How to sort Alphanumeric String

javasorting

提问by Thaven

I have a problem with sorting strings which include integers. If I use the below code I get sorting like: 1some, 2some, 20some, 21some, 3some, some

我对包含整数的字符串进行排序有问题。如果我使用下面的代码,我会得到这样的排序:1some, 2some, 20some, 21some, 3some, some

However I want it sorted like: 1some, 2some, 3some, 20some, 21some, some

但是我希望它排序为:1some, 2some, 3some, 20some, 21some, some

How can I do this?

我怎样才能做到这一点?

Thanks!

谢谢!

Collections.sort(selectedNodes,
    new Comparator<DefaultMutableTreeNode>() {
    @Override
    public int compare(DefaultMutableTreeNode o1,
        DefaultMutableTreeNode o2) {
        return o1.getUserObject().toString()
            .compareTo(o2.getUserObject().toString());
    }
    });

采纳答案by Mena

Here is a self-contained example on how to do this (not particularly optimized):

这是一个关于如何执行此操作的独立示例(未特别优化):

final Pattern p = Pattern.compile("^\d+");
String[] examples = { 
   "1some", "2some", "20some", "21some", "3some", "some", "1abc", "abc"
};
Comparator<String> c = new Comparator<String>() {
    @Override
    public int compare(String object1, String object2) {
        Matcher m = p.matcher(object1);
        Integer number1 = null;
        if (!m.find()) {
            return object1.compareTo(object2);
        }
        else {
            Integer number2 = null;
            number1 = Integer.parseInt(m.group());
            m = p.matcher(object2);
            if (!m.find()) {
                return object1.compareTo(object2);
            }
            else {
                number2 = Integer.parseInt(m.group());
                int comparison = number1.compareTo(number2);
                if (comparison != 0) {
                    return comparison;
                }
                else {
                    return object1.compareTo(object2);
                }
            }
        }
    }
};
List<String> examplesList = new ArrayList<String>(Arrays.asList(examples));
Collections.sort(examplesList, c);
System.out.println(examplesList);

Output

输出

[1abc, 1some, 2some, 3some, 20some, 21some, abc, some]

Explanation

解释

  • The example uses a constant Patternto infer whether a number is in the String's starting position.
  • If not present in the first String, it compares it as is to the second.
  • If present indeed in the first, it checks the second.
  • If not present in the second, it compares the two Strings as is, again
  • If present in both, it compares the Integers instead of the whole Strings, hence resulting in a numerical comparison rather than a lexicographical one
  • If the number compare identical, it goes back to lexicographic comparison of the whole Strings (thanks MihaiCfor spotting this one)
  • 该示例使用一个常量Pattern来推断一个数字是否在String的起始位置。
  • 如果第一个中不存在String,则将其与第二个进行比较。
  • 如果确实存在于第一个中,它会检查第二个。
  • 如果第二个中不存在,则String再次按原样比较两个s
  • 如果两者都存在,则比较Integers 而不是整个Strings,从而导致数字比较而不是字典比较
  • 如果数字比较相同,则返回到整个Strings 的字典比较(感谢MihaiC发现这个)

回答by Murtaza Khursheed Hussain

Your solution lies in The Alphanum Algorithmand you can implement like this

您的解决方案在于Alphanum 算法,您可以像这样实现

回答by Zhedar

You need to implement your own Comparatorto do this kind of custom sorting. The default String.compareTo()method seems to sort numbers before characters. When 0in 20somegets compared to sin 3somethe 0has a higher sort priority and therefore the whole word gets sorted in first.
What you would need to do is this: try to split you strings into the number and the character part. That's a hard task since those Strings can consist of many of those parts (or don't they?). You may use algorithms like Alphanum, which Murtaza already showed to you.
If you want to implement it yourself, you could check, where the number part ends. Then parse it to an intwith Integer.parse(). Compare intparts if they exist in both Strings, then compare the rest. Well that may not be the most professional solution, but as a beginner you may want craft those things yourself to learn it.

您需要自己实现Comparator来进行这种自定义排序。默认String.compareTo()方法似乎在字符之前对数字进行排序。当020some被比较,s3some0具有较高的优先级排序,因此整个单词获取第一排序。
您需要做的是:尝试将字符串拆分为数字和字符部分。这是一项艰巨的任务,因为这些Strings 可以由许多这些部分组成(或者不是?)。您可以使用像AlphanumMurtaza 已经向您展示的算法。
如果你想自己实现它,你可以检查,数字部分在哪里结束。然后将其解析为intwith Integer.parse()。比较int两者中是否存在零件Strings,然后比较其余的。好吧,这可能不是最专业的解决方案,但作为初学者,您可能希望自己制作这些东西来学习它。

回答by Vishu

    String [] str = new String[]{"1some", "2some", "20some", "21some", "3some", "some"};
    List<String> list = Arrays.asList(str);

    Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
    System.out.println(list);

回答by Roshith

You can't use the default String compareTo() instead need compare the Strings following the below algorithm.

您不能使用默认的 String compareTo() 而是需要按照以下算法比较字符串。

  1. Loop through the first and second String character by character and get a chunk of all strings or numbers
  2. Check if the chunks are numbers or strings
  3. If numbers sort numerically else use String compareTo()
  1. 逐个字符循环遍历第一个和第二个 String 并获取所有字符串或数字的块
  2. 检查块是数字还是字符串
  3. 如果数字按数字排序,否则使用 String compareTo()

Repeat the steps.

重复这些步骤。

回答by Joop Eggen

First make an alphanumerical comparator splitting the string in String or Integer parts.

首先制作一个字母数字比较器,将字符串分成字符串或整数部分。

public class AlphaNumericalComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        List<Object> parts1 = partsOf(o1);
        List<Object> parts2 = partsOf(o2);
        while (!parts1.isEmpty() && !parts2.isEmpty()) {
            Object part1 = parts1.remove(0);
            Object part2 = parts2.remove(0);
            int cmp = 0;
            if (part1 instanceof Integer && part2 instanceof Integer) {
                cmp = Integer.compare((Integer)part1, (Integer)part2);
            } else if (part1 instanceof String && part2 instanceof String) {
                cmp = ((String) part1).compareTo((String) part2);
            } else {
                cmp = part1 instanceof String ? 1 : -1; // XXXa > XXX1
            }
            if (cmp != 0) {
                return cmp;
            }
        }
        if (parts1.isEmpty() && parts2.isEmpty()) {
            return 0;
        }
        return parts1.isEmpty() ? -1 : 1;
    }

    private List<Object> partsOf(String s) {
        List<Object> parts = new LinkedList<>();
        int pos0 = 0;
        int pos = 0;
        boolean wasDigit = false;
        while (true) {
            if (pos >= s.length()
                    || Character.isDigit(s.charAt(pos)) != wasDigit) {
                if (pos > pos0) {
                    String part = s.substring(pos0, pos);
                    parts.add(wasDigit? Integer.valueOf(part) : part);
                    pos0 = pos;
                }
                if (pos >= s.length()) {
                    break;
                }
                wasDigit = !wasDigit;
            }
            ++pos;
        }
        return parts;
    }
};

Then use this comparator in your own one, in Java 8 you may simply use Comparator's static methods.

然后在你自己的比较器中使用这个比较器,在 Java 8 中你可以简单地使用 Comparator 的静态方法。

回答by Bohemian

You can do the core of it in one line using regex to extract the numeric part:

您可以使用正则表达式在一行中完成它的核心以提取数字部分:

Collections.sort(selectedNodes, new Comparator<DefaultMutableTreeNode>() {
    @Override
    public int compare(DefaultMutableTreeNode o1,
        DefaultMutableTreeNode o2) {
        return Integer.parseInt(o1.getUserObject().toString().replaceAll("\D", "")) -
            Integer.parseInt(o2.getUserObject().toString().replaceAll("\D", ""));
    }
});

回答by Akshay Nair

This is a working solution is Java. If you have any suggestions on the code, please let me know on my Gist.

这是一个有效的解决方案是 Java。如果您对代码有任何建议,请在我的Gist上告诉我。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class FB {

    public static int comparator(String s1, String s2) {

        String[] pt1 = s1.split("((?<=[a-z])(?=[0-9]))|((?<=[0-9])(?=[a-z]))"); 
        String[] pt2 = s2.split("((?<=[a-z])(?=[0-9]))|((?<=[0-9])(?=[a-z]))"); 
//pt1 and pt2 arrays will have the string split in alphabets and numbers

        int i=0;
        if(Arrays.equals(pt1, pt2))
            return 0;
        else{
            for(i=0;i<Math.min(pt1.length, pt2.length);i++)
                if(!pt1[i].equals(pt2[i])) {
                    if(!isNumber(pt1[i],pt2[i])) {
                        if(pt1[i].compareTo(pt2[i])>0)
                            return 1;
                        else
                            return -1;
                    }
                    else {
                        int nu1 = Integer.parseInt(pt1[i]);
                        int nu2 = Integer.parseInt(pt2[i]);
                        if(nu1>nu2)
                            return 1;
                        else
                            return -1;
                    }
                }
        }

        if(pt1.length>i)
            return 1;
        else
            return -1;
    }

    private static Boolean isNumber(String n1, String n2) {
        // TODO Auto-generated method stub
        try {
            int nu1 = Integer.parseInt(n1);
            int nu2 = Integer.parseInt(n2);
            return true;
        }
        catch(Exception x) {
            return false;
        }

    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String[] examples = {"1some", "2some", "20some", "21some", "3some", "some", "1abc", "abc"};
        List<String> values = new ArrayList<String>(Arrays.asList(examples));

        System.out.println(values);
        Comparator<String> com = (o1,o2) -> {return comparator(o1,o2);}; //lambda expression

        Collections.sort(values,com);
        System.out.println(values);
    }
}

Output:

输出:

[1some, 2some, 20some, 21some, 3some, some, 1abc, abc]
[1abc, 1some, 2some, 3some, 20some, 21some, abc, some]

回答by HRgiger

If you know that pattern is always NUMALPHA or ALPHANUM and alpha always same:

如果您知道该模式始终是 NUMALPHA 或 ALPHANUM 并且 alpha 始终相同:

if(str1.length() != str2.length()){
   return str1.length() - str2.length();
}

return str1.compareTo(str2);

回答by Yash Srivastav

If you have alphanumeric string array you can directly sort it by using

如果您有字母数字字符串数组,则可以直接使用

Arrays.sort(Array_name)

and then to print:

然后打印:

for(String a : Array_name)
    System.out.print(a);