【问题标题】:Java: Compare Strings with keywords in different orderJava:以不同顺序将字符串与关键字进行比较
【发布时间】:2017-09-04 12:27:49
【问题描述】:

我有两个如下所示的字符串:

String str1 = "[0.7419,0.7710,0.2487]";
String str2 = "[\"0.7710\",\"0.7419\",\"0.2487\"]";

我想比较它们,尽管顺序不同,但它们是相等的......

哪种方法是最快、最简单的方法?

我应该将每个数组拆分为数组并比较两个数组吗?或不? 我想我必须删除 "[","]",""" 字符以使其更清晰,所以我这样做了。我还用 " " 替换了 "," 但我不知道这是否有帮助......

提前致谢:)

编辑:我的字符串并不总是一组双精度或浮点数。它们也可能是实际的单词或一组字符。

【问题讨论】:

  • 你的字符串代表Set<Double>(至少你是这么说的)。所以解析它们,然后比较。
  • 字符串中是否只有数字?或者它可以是任何字符?
  • 我会将这些数字解析为 Double 并将它们放入 List。然后一一排序比较。
  • 这只是我发布的一个示例。它们并不总是双精度数或浮点数,它们也可能是一组字符。不过谢谢!
  • 一个更慢但更好的解决方案是将它们读取为 JSONArrays。因为这就是他们的样子。也许那时比较它们会很容易

标签: java string compare keyword


【解决方案1】:

因为你有一个混合的结果类型,你需要先把它当作混合输入来处理

这是我将如何替换它,尤其是对于较长的字符串。

private Stream<String> parseStream(String in) {
    //we'll skip regex for now and can simply hard-fail bad input later
    //you can also do some sanity checks outside this method
    return Arrays.stream(in.substring(1, in.length() - 1).split(",")) //remove braces
        .map(s -> !s.startsWith("\"") ? s : s.substring(1, s.length() - 1)); //remove quotes
}

接下来,我们现在有一个字符串流,需要将其解析为原语或字符串(因为我假设我们没有某种奇怪的对象序列化形式):

private Object parse(String in) {
    //attempt to parse as number first. Any number can be parsed as a double/long
    try {
        return in.contains(".") ? Double.parseDouble(in) : Long.parseLong(in);
    } catch (NumberFormatException ex) {
        //it's not a number, so it's either a boolean or unparseable
        Boolean b = Boolean.parseBoolean(in); //if not a boolean, #parseBoolean is false
        b = in.toLowerCase().equals("false") && !b ? b : null; //so we map non-false to null
        return b != null ? b : in; //return either the non-null boolean or the string
    }
}

使用它,我们可以将混合流转换为混合集合:

Set<Object> objs = this.parseStream(str1).map(this::parse).collect(Collectors.toSet());
Set<Object> comp = this.parseStream(str2).map(this::parse).collect(Collectors.toSet());
//we're using sets, keep in mind the nature of different collections and how they compare their elements here
if (objs.equals(comp)) {
    //we have a matching set
}

最后,一些健全性检查的示例是确保输入字符串上有适当的大括号等。尽管其他人说我学习了集合语法为{a, b, ...c},而系列/列表语法为[a, b, ...c] ,两者在这里有不同的比较。

【讨论】:

  • 解析成 Long、Double 或 Boolean 而不是简单地将所有内容保存为字符串有什么好处?
  • 同一个值的不同数字在解析时不会匹配为字符串,例如1237.01237.00.
  • 公平点。下一个问题:您的意思是substring(1) 而不是substring(0)? (后者只返回this。)
  • 哈,是的,我做到了。没有什么比早上好的,固定的。
【解决方案2】:

这可以通过下面的方法来完成,该方法使用 TreeSet 实现了一组字符串,因此可以内置处理排序。它只是一个简单的转换字符串集合和使用equals方法进行比较。 试试下面的代码:

String str1 = "[0.7419,0.7710,0.2487]";
        String str2 = "[\"0.7710\",\"0.7419\",\"0.2487\"]";
        String jsonArray = new JSONArray(str2).toString();
        Set<String> set1 = new TreeSet<String>(Arrays.asList(str1.replace("[", "").replace("]", "").split(",")));
        Set<String> set2 = new TreeSet<String>(Arrays.asList(jsonArray.replace("[", "").replace("]", "").replace("\"", "").split(",")));
        if(set1.equals(set2)){
             System.out.println(" str1 and str2 are equal");
       }

在上面的代码中,我借助 jsonArray 来删除 "\" 字符。

注意:

但是如果一个字符串中有重复的元素而另一个 string 的数量不同,因为 set 不保留重复项。

尝试使用 list 保留重复元素并解决您的问题。

String str1 = "[0.7419,0.7710,0.2487]";
            String str2 = "[\"0.7710\",\"0.7419\",\"0.2487\"]";
            String jsonArray = new JSONArray(str2).toString();
            List<String> list1=new ArrayList<String>(Arrays.asList(str1.replace("[", "").replace("]", "").split(",")));
            List<String> list2=new ArrayList<String>(Arrays.asList(jsonArray.replace("[", "").replace("]", "").replace("\"", "").split(",")));
            Collections.sort(list1);
            Collections.sort(list2);
            if(list1.equals(list2)){
                  System.out.println("str1 and str2 are equal");
            }

【讨论】:

  • 如果删除set1set2 为空,并不意味着它们相等。这可能意味着set2set1 的子集。请改用set1.equals(set2)
  • 怎么可能? , 如果 set1 的所有元素都从 set2 中删除,则如果它为空,则它们相等,否则不相等。
  • 如果set2{1, 2, 3} 并且set1{1, 2, 3, 4},那么从set2 中删除set1 会给你一个空集。但他们仍然不相等
【解决方案3】:

像这样:

    String[] a1 = str1.replaceAll("^\\[|\\]$", "").split(",", -1);
    String[] a2 = str2.replaceAll("^\\[|\\]$", "").split(",", -1);
    for (int i = 0; i < a2.length; i++)
        a2[i] = a2[i].replaceAll("^\\\"|\\\"$", "");
    Arrays.sort(a1);
    Arrays.sort(a2);
    boolean stringsAreEqual = Arrays.equals(a1, a2);

或者您可以使用功能齐全的方法(可能效率稍低):

    boolean stringsAreEqual = Arrays.equals(
            Arrays.stream(str1.replaceAll("^\\[|\\]$", "").split(",", -1))
                    .sorted()
                    .toArray(),
            Arrays.stream(str2.replaceAll("^\\[|\\]$", "").split(",", -1))
                    .map(s -> s.replaceAll("^\\\"|\\\"$", ""))
                    .sorted()
                    .toArray()
    );

使用数组相对于使用集合的优势(正如其他人所建议的那样)是数组通常使用较少的内存并且它们可以保存重复项。如果您的问题域可以在每个字符串中包含重复元素,则不能使用集合。

【讨论】:

  • 对于大量元素,这是低效的解决方案。当您对两个数组进行排序时,您只需要检查两个数组是否相等。
  • 顺便说一句,如果使用 java.util.HashSet 是由于无法处理重复而导致的问题,则可以将其替换为 Google Guava HashMultiset:google.github.io/guava/releases/22.0/api/docs/com/google/common/…
【解决方案4】:

这是使用 HashSet 的非常简单的解决方案。

套装的好处:-

  • 不能包含重复项。
  • 元素的插入/删除是 O(1)。
  • 比数组快得多。这里保持元素 Order 也是 不重要所以没关系。

    String str1 = "[0.7419,0.7710,0.2487]";
    String str2 = "[\"0.7710\",\"0.7419\",\"0.2487\"]";
    
    Set<String> set1 = new HashSet<>();
    Set<String> set2 = new HashSet<>();
    
    String[] split1 = str1.replace("[", "").replace("]", "").split(",");
    String[] split2 = str2.replace("[", "").replace("]", "").replace("\"", "").split(",");
    set1.addAll(Arrays.asList(split1));
    set2.addAll(Arrays.asList(split2));
    
    System.out.println("set1: "+set1);
    System.out.println("set2: "+set2);
    
    boolean isEqual = false;
    if(set1.size() == set2.size()){
        set1.removeAll(set2);
        if(set1.size() ==0){
            isEqual = true;
        }
    }
    
    System.out.println("str1 and str2 "+( isEqual ? "Equal" : "Not Equal") );
    

输出:

set1: [0.7710, 0.2487, 0.7419]
set2: [0.7710, 0.2487, 0.7419]
str1 and str2 Equal

【讨论】:

  • 如果set2 包含set1 的所有元素以及更多元素,您将无法区分两个集合是否相同。
  • 或者只是子串然后拆分一次。对于更长的字符串,没有迭代。
  • @nagendra547 这个建议不仅错误而且很危险。 #equals/#hashcode 是语言的关键结构。 #equals(对于HashSet)将使用所有这些比较(例如大小),然后是#containsAll,它将使用O(1) 包含来自HashSet 的方法在 passed的元素上> 集合(不是父级)。使用#removeAll 将迭代输入集合并在HashSet 上调用(也)O(1)#remove 方法。因此,就您关于“效率低下”的论点而言,这是热空气。你只是在重新发明轮子。
  • @nagendra547 SO 问题与您手头的评论无关,它在集合本身上使用#equals(未在对象中实现哈希性/平等)。
  • 是的,正如我在上面的评论中从字面上描述的那样,您的理由是什么? 什么你觉得这么“低效”/慢?因为从我所看到的情况来看,您以不必要地修改集合+重新发明轮子+更多代码为代价并没有节省任何效率。我会称之为过早优化,但我认为在这种情况下它甚至不会优化。
【解决方案5】:

Google GSON 可以通过将值读取为Set&lt;String&gt; 来相当巧妙地处理此任务:

    final String str1 = "[0.7419,0.7710,0.2487]";
    final String str2 = "[\"0.7710\",\"0.7419\",\"0.2487\"]";
    final String str3 = "[\"0.3310\",\"0.7419\",\"0.2487\"]";
    final Gson gson = new Gson();
    final Type setOfStrings = new TypeToken<Set<String>>() {}.getType();
    final Set<String> set1 = gson.fromJson(str1, setOfStrings);
    final Set<String> set2 = gson.fromJson(str2, setOfStrings);
    final Set<String> set3 = gson.fromJson(str3, setOfStrings);

    System.out.println("Set #1:" + set1);
    System.out.println("Set #2:" + set2);
    System.out.println("Set #3:" + set3);
    System.out.println("Set #1 is equivalent to Set #2: " + set1.equals(set2));
    System.out.println("Set #1 is equivalent to Set #3: " + set1.equals(set3));

输出是:

Set #1:[0.7419, 0.7710, 0.2487]
Set #2:[0.7710, 0.7419, 0.2487]
Set #3:[0.3310, 0.7419, 0.2487]
Set #1 is equivalent to Set #2: true
Set #1 is equivalent to Set #3: false

【讨论】:

    猜你喜欢
    • 2017-02-20
    • 2010-10-06
    • 2018-10-05
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 2012-08-31
    • 2011-10-19
    • 1970-01-01
    相关资源
    最近更新 更多