【问题标题】:sorting data in specific order按特定顺序对数据进行排序
【发布时间】:2017-12-20 12:14:28
【问题描述】:

我的 rowId 如下所示

1

1.1

1.2

.

1.9

2

2.1

.

。 3 . . 9

.

9.9

10

10.1

List<MyBean> sortedList = rootItems.stream().sorted(Comparator.comparing(MyBean::getRowId)) .collect(Collectors.toList());

通过使用上面的代码,我得到如下输出。

1

10

11

12

2

2.1

2.2

.

.

但我想要如下输出

1

1.1

1.1.1

1.1.1.1

1.2

1.3

.

.

.

1.9

10

11

.

2

2.1

2.2

2.3

.

.

3

3.1

3.2

.

.

4

.

.

.

9

9.1

9.2

.

.

9.9

10

10.1

这样它需要继续

如果我想要这样,我需要使用哪个 Set

【问题讨论】:

  • 您的问题包含不必要的行,请您删除它们
  • getRowId的返回类型是什么?对我来说好像是String
  • 在 sort/comapre 部分解析为数字(double ex.)。
  • 将您的字符串解析为数字序列,然后进行比较。字符串逐个字符进行比较,因此1.1 大于10,因为. 大于0。或者更改您的 id 以使所有元素具有相同的长度,例如通过前置零。因此1.1 可能变成01.01(甚至更多的零)等等。
  • @daniu 是的,rowId的返回类型是String

标签: java collections set


【解决方案1】:

如果您在内部将这些字符串标准化为可以排序的格式,那么比较这些字符串会容易得多。我建议用零填充各个项目,如下所示:

private static final Pattern SINGLE_DIGIT = Pattern.compile("\\b(\\d)\\b");
...
String padWithZeroes = SINGLE_DIGIT.matcher(yourInputString).replaceAll("0$1");

现在只需对填充的字符串进行排序。 这是假设最高的数字是2位,如果不是,我们需要调整正则表达式,逻辑会稍微复杂一些。


如果任何数字段可以长于 2 位,那么它会变得更复杂。这是一种将所有段填充到最大长度的方法:

static String padWithZeroes(String yourInputString, int digits) {
    final Matcher matcher = SINGLE_DIGIT.matcher(yourInputString);
    StringBuffer sb = new StringBuffer();
    while(matcher.find()){

        matcher.appendReplacement(sb, pad(digits - matcher.group().length())+matcher.group());
    }
    matcher.appendTail(sb);
    return sb.toString();
}

static String pad(int length) {
    final char[] chars = new char[length];
    Arrays.fill(chars, '0');
    return new String(chars);
}

这里的问题是,您必须预先知道 digits 参数的值,因此您要么知道自己的数据,要么必须进行单独的迭代来测量最大长度。

【讨论】:

    【解决方案2】:

    我没有在我的电脑上尝试过,但似乎数据没有被转换成双倍进行比较。我认为在排序之前添加mapToDouble(i-&gt;i.getRowId) 可以解决您的问题。

    【讨论】:

    • 好吧,OP 只列出了 2 个级别,但我很确定他最终会有 3 个或更多级别,即 1.1.1 - 解析为 double 会很快失败。
    • 从这个问题来看,输入或输出数据并没有明确定义,如果他确实打算这样做,他必须按照其他人的建议实现自己的比较器。
    • @NareshDonthula 正如我在之前的评论中所说的那样,您需要实现自己的比较器。在高层次上,我将如何处理它,将字符串拆分为点,将它们转换为整数迭代并通过生成的数组进行比较。
    【解决方案3】:

    正如我在评论中已经说过的,问题是字符串是逐个字符进行比较的。因此,您需要更改字符串以使每个元素具有相等的长度并且每个字符位置对应于其他 id 中的相同位置,或者将元素解析为数字序列

    备选方案 1 的缺点是您需要为每个组件(即 a.b.c 中的 a、b、c 等)设置一个固定的最大长度,或者您必须先计算元素的数量。

    因此,备选方案 2 似乎更好,但每次比较字符串时解析字符串可能会有点昂贵。因此,您可能想要引入一个代表您的 id 并保存实际 id 字符串以及已解析版本的类。该类可以实现Comparable 并仅在解析后的版本上运行。

    例子:

    class MyRowId implements Comparable<MyRowId> {
      private final String id;
    
      //when serializing instances of that class you might want to ignore this array 
      //and upon deserialization parse the string again
      private final int[] parsed;
    
      public MyRowId( String idString ) {
        id = idString;
        parsed = Arrays.stream( idString.split("\\.") ).mapToInt( Integer::parseInt ).toArray();
      }
    
      public int compareTo( MyRowId other )  {
        //Compare the elements one by one until we run out of elements or hit a difference
        //There might be a more Java8-ish way but I didn't find it yet
        for( int i = 0; i < Math.min( parsed.length, other.parsed.length ); i++ ) {
          int r = Integer.compare( parsed[i], other.parsed[i] );
          if( r != 0 ) {
            return r;
          }
        }
    
        //If we're here all compared elements were equal but one id might be longer so compare the lengths.
        //That would handle things like comparing "1.1" to "1.1.1" where the latter is greater.
        return Integer.compare( parsed.length, other.parsed.length );
      }
    }
    

    【讨论】:

    • 在我的问题中,rootItems 是 Set rootItems = new HashSet();
    • @NareshDonthula 抱歉,我在工作,所以没有那么多时间。
    • @NareshDonthula 就像使用当前正在使用的字符串一样使用它。假设 MyBean 有一个字段 rowId 使该字段的类型为 MyRowId 而不是字符串。
    • 如果我在 MyBean 中更改 rowId 会影响保存功能
    • 从为目标上下文计算的描述符中的 MyRowId 类型在此处不可见。
    【解决方案4】:

    像这样使用自己的compare() 方法:

    Collections.sort(myList, new Comparator<String>() {
        @Override
        public int compare(String id1, String id2) {
            double d1 = Double.parseDouble(id1);
            double d2 = Double.parseDouble(id2);
            return Double.compare(d1, d2);
        }
    });
    

    或者只是将 ID 解析/映射到双精度然后比较/排序。

    【讨论】:

    • 好吧,OP 只列出了 2 个级别,但我很确定他最终会有 3 个或更多级别,即 1.1.1 - 解析为 double 会很快失败。
    • 然后他可以用''替换所有'.'并将完整的数字解析为int/double并将'0'添加到6个字母,所以1.1.992.0因为119900 200000 - 很容易达到 X 个字母和级别
    • 嗯,1.1.99 已经 小于 2.0。考虑2.1.110.1.1:您的方法将导致211000101100 - 也不起作用。
    【解决方案5】:

    如果您指定了 getRowId() 的返回类型,那么实现可能会有所不同。


    您可能需要做的是为 MyBean 类定义一个 Comparator。它可以实现为

    • 匿名类
    • lambda 表达式。

    你的情况可能是:

    class MyBeanComparator implements Comparator<MyBean>{
      public static final MyBeanComparator INSTANCE = new MyBeanComparator();
      public int compare(MyBean b1, MyBean b2){
             //your custom rules
          }
    }
    

    然后

    List<MyBean> sortedList = rootItems.stream().sorted(MyBeanComparator.INSTANCE::compare) .collect(Collectors.toList());
    

    【讨论】:

    • 好吧,他的问题是他会将compare(MyBean b1, MyBean b2) 实现为b1.getId().compareTo(b2),所以这根本不能回答问题。
    猜你喜欢
    • 2016-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-04
    • 1970-01-01
    相关资源
    最近更新 更多