我有一个包含这样的数据的字符串向量:
5:34,5:38,17:21,22:11,......
如果我尝试使用 Collections.sort(...); 合并它,它将显示如下:
17:21,22:11,5:34,5:38
实际上我希望它看起来像这样:
5:34,5:38,17:21,22:11
所以我想根据冒号“:”之前的数字对元素进行排序,然后如果某些元素在“:”之前具有相同的数字,则根据“:”之后的数字对它们进行排序。
最简单的方法是什么?
答案 0 :(得分:7)
正确的方法是不将非字符串值存储为字符串。
集合中的数据具有一些结构和规则,不能是任意字符串。因此,您不应使用String
数据类型。
让我们定义一个名为TwoNumbers
的类型(因为我不知道该类型应该代表什么,即使我可以猜到):
class TwoNumbers implements Comparable<TwoNumbers> {
private final int num1;
private final int num2;
public TwoNumbers(int num1, int num2) {
if (num1 <= 0 || num2 <= 0) {
throw new IllegalArgumentException("Numbers must be positive!");
}
this.num1 = num1;
this.num2 = num2;
}
public static TwoNumbers parse(String s) {
String[] parts = s.split(":");
if (parts.length != 2) {
throw new IllegalArgumentException("String format must be '<num>:<num>'");
}
try {
return new TwoNumbers(Integer.parseInt(parts[0]), Integer.parseInt(parts[0]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("parts must be numeric!", e);
}
}
public int getNum1() {
return num1;
}
public int getNum2() {
return num2;
}
@Override
public int compareTo(TwoNumbers o) {
if (o == null) {
return 1;
}
int diff = Integer.compare(o.num1, this.num1);
if (diff == 0) {
diff = Integer.compare(o.num2, this.num2);
}
return diff;
}
}
compareTo
方法作为the Comparable
interface的实现存在:它定义了如何对这种类型的对象进行排序。
我使用了final
字段(并且不提供setter),因为该类实现了immutable objects。
通过这种方式,您可以直接对数据进行排序而无需额外的Comparator
,并且无需在整个程序中分发所有“拆分和解析”代码。相反,你有一个单类负责处理这种特定的格式,所有其他代码片段都可以使用它。
答案 1 :(得分:4)
这非常低效,但应该可以胜任。
Collections.sort(data, new Comparator<String>(){
public int compare(String a, String b){
String[] as = a.split(":");
String[] bs = b.split(":");
int result = Integer.valueOf(as[0]).compareTo(Integer.valueOf(bs[0]));
if(result==0)
result = Integer.valueOf(as[1]).compareTo(Integer.valueOf(bs[1]));
return result;
}
})
(提示:如果是我的代码,我会优化它以使用子字符串而不是String.split(),但我太懒了)
答案 2 :(得分:2)
您可以创建自定义Comparator
来拆分String
并将其解析为两个整数,或创建一个定制的类来表示每个String
和将其存储在Collection
中。我赞成后一种方法,因为你只需要分配/解析一次String的开销; e.g。
public class Data implements Comparable<Data> {
private final int prefix;
private final int suffix;
public Data(String str) {
String[] arr = str.split(":");
if (arr.length != 2) {
throw new IllegalArgumentException();
}
this.prefix = Integer.parseInt(arr[0]);
this.suffix = Integer.parseInt(arr[1]);
}
public int compareTo(Data data) {
// Should really avoid subtraction in case of overflow but done to keep code brief.
int ret = this.prefix - data.prefix;
if (ret == 0) {
ret = this.suffix - data.suffix;
}
return ret;
}
// TODO: Implement equals and hashCode (equals to be consistent with compareTo).
public String toString() { return String.format("%d:%d", prefix, suffix); }
}
然后,只是在Data
中存储一些Collection
个对象的情况; e.g。
List<Data> l = new ArrayList<Data>();
l.add(new Data("13:56"));
l.add(new Data("100:16"));
l.add(new Data("9:1"));
Collections.sort(l);
还有一件事 - 你提到你正在使用Vector
。您应该尽量避免使用Vector
/ Hashtable
,因为这些已被List
/ Map
取代,这些是作为JDK 1.2中的集合框架的一部分引入的。
答案 3 :(得分:0)
创建java.util.Comparator
并将其提供给sort
方法。
答案 4 :(得分:0)
实施您自己的Comparator
类,比较两个值并调用Collections.sort(List list, Comparator c)
。
答案 5 :(得分:0)
实现您自己的 Comparator ,并将其作为Colelctions.sort方法的第二个参数。
答案 6 :(得分:0)
通常,将Java中的对象(包括集合)与其默认的hashCode()和equals()方法进行比较。对于内置对象和数据类型(如String,Integet等),hashCode()在内部计算,因此它们被JLS(Java语言规范)保证使用。
由于我们不能总是依赖于默认/内置对象,我们需要处理自己的自定义对象(如Employee,Customer等),我们应该重写hashCode()和equals()方法,以便我们可以根据自定义类的对象的“BEST”相等来提供true / false。
类似地,sort()涉及一个比较行为,它确实需要一个Comparator(它是一个用比较方法的重写方法实现Comparator接口的类)。您还应该覆盖比较方法,该方法需要比较两个对象并返回结果(0表示相等,1表示第一个对象大于第二个,2表示情况1的反向)。
现在,您应该以不同的方式处理数据,这远远不同于正常的比较。你需要将数据分成两部分(使用你可以使用的分割方法),然后你可以对两个parats进行单独的比较(冒号前的第一部分,冒号后的第二部分)。
最后,您应该为sort方法提供此自定义比较器的实例,最终将对您的自定义数据进行自定义排序:)
答案 7 :(得分:0)
我认为这很简单:
public class NumericalStringSort {
public static void main(String[] args) {
List<String> input = Arrays.asList(new String[] {"17:21", "22:11", "5:34", "5:38"});
Collections.sort(input, new NumericalStringComparator());
System.out.println(input);
}
public static class NumericalStringComparator implements Comparator<String> {
public int compare(String object1, String object2) {
return pad(object1).compareTo(pad(object2));
}
private String pad(String input) {
return input.indexOf(":") == 1 ? "0" + input : input;
}
}
}
答案 8 :(得分:0)
刚发现这个(相当古老)的帖子,答案并没有解决我的问题。我需要一个更通用的解决方案,因为值是用户输入和类似&#34; abc 1 a 12&#34;和&#34; abc 1 a 1&#34;应按所包含的数字的顺序排序。所以我写了下面的比较器:
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
String[] s1=splitNumeric(o1);
String[] s2=splitNumeric(o2);
for (int x=0;x<s1.length&&x<s2.length;x++){
if (!s1[x].equals(s2[x])){
if (s1[x].charAt(0)=='N' && s2[x].charAt(0)=='N'){
long l1=Long.parseLong(s1[x].substring(1));
long l2=Long.parseLong(s2[x].substring(1));
return (int)Math.signum(l1-l2);
}
break;
}
}
return o1.compareTo(o2);
}
}
虽然函数splitNumeric定义如下:
private String[] splitNumeric(String s){
final String numbers="0123456789";
LinkedList<String> out=new LinkedList<String>();
int state=-1;
for (int x=0;x<s.length();x++){
if (numbers.contains(s.charAt(x)+"")){
if (state==1)
out.set(out.size()-1,out.getLast()+s.charAt(x));
else{
state=1;
out.add("N"+s.charAt(x));
}
}
else{
if (state==0)
out.set(out.size()-1,out.getLast()+s.charAt(x));
else{
state=0;
out.add("S"+s.charAt(x)+"");
}
}
}
return out.toArray(new String[0]);
}
代码将对字符串
进行排序"X 124 B"
"X 1 Y"
"X 111 Z"
"X 12 Y"
"12:15"
"12:13"
"12:1"
"1:1"
"2:2"
如下:
"1:1"
"2:2"
"12:1"
"12:13"
"12:15"
"X 1 Y"
"X 12 Y"
"X 111 Z"
"X 124 B"
享受:)