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
How to sort Alphanumeric String
提问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
Pattern
to infer whether a number is in theString
'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
String
s as is, again - If present in both, it compares the
Integer
s instead of the wholeString
s, 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
String
s (thanks MihaiCfor spotting this one)
- 该示例使用一个常量
Pattern
来推断一个数字是否在String
的起始位置。 - 如果第一个中不存在
String
,则将其与第二个进行比较。 - 如果确实存在于第一个中,它会检查第二个。
- 如果第二个中不存在,则
String
再次按原样比较两个s - 如果两者都存在,则比较
Integer
s 而不是整个String
s,从而导致数字比较而不是字典比较 - 如果数字比较相同,则返回到整个
String
s 的字典比较(感谢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 Comparator
to do this kind of custom sorting. The default String.compareTo()
method seems to sort numbers before characters. When 0
in 20some
gets compared to s
in 3some
the 0
has 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 String
s 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 int
with Integer.parse()
. Compare int
parts if they exist in both String
s, 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()
方法似乎在字符之前对数字进行排序。当0
在20some
被比较,s
在3some
对0
具有较高的优先级排序,因此整个单词获取第一排序。
您需要做的是:尝试将字符串拆分为数字和字符部分。这是一项艰巨的任务,因为这些String
s 可以由许多这些部分组成(或者不是?)。您可以使用像Alphanum
Murtaza 已经向您展示的算法。
如果你想自己实现它,你可以检查,数字部分在哪里结束。然后将其解析为int
with Integer.parse()
。比较int
两者中是否存在零件String
s,然后比较其余的。好吧,这可能不是最专业的解决方案,但作为初学者,您可能希望自己制作这些东西来学习它。
回答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() 而是需要按照以下算法比较字符串。
- Loop through the first and second String character by character and get a chunk of all strings or numbers
- Check if the chunks are numbers or strings
- If numbers sort numerically else use String compareTo()
- 逐个字符循环遍历第一个和第二个 String 并获取所有字符串或数字的块
- 检查块是数字还是字符串
- 如果数字按数字排序,否则使用 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);