【问题标题】:convert List<String> to Set in CASE INSENSITIVE Manner-Java 6以 CASE INSENSITIVE 方式-Java 6 将 List<String> 转换为 Set
【发布时间】:2015-11-16 17:11:58
【问题描述】:

我有一个包含字符串 ABC:123,abc:123 的列表;当我将它转换为 Set 时,它给了我 2 个不同的元素。是否有一种方法可以将此列表转换为 Set 忽略大小写,以便我的Set 仅包含 ABC:123。但如果输入 List 包含 ABC:123a4,abc:1234A4 它应该在 Set 中给我 2 个不同的元素:ABC:123a4,ABC:1234A4 我知道这可以通过首先拆分“:”上的列表元素并将 abc 转换为全部大写并将它们添加到新列表然后其余部分来完成。但只是想知道是否有更好的方法(小行代码)这样做。提前感谢您提供任何头脑风暴的想法。

List<String> memlist = new ArrayList<String>(Arrays.asList(memberList.split(",")));
Set<String> memberSet = new HashSet<String>(memlist );
memlist = new ArrayList<String>(memberSet);

【问题讨论】:

  • 要么扩展 Set 并覆盖其 add (E e) 方法;或围绕它的包装。在这种情况下,我推荐第一个。 @redflar3 所说的同样有效。
  • 会在插入前将所有字符串转换为常见大小写解决您的问题吗?
  • @redflar3 根据他对 SQL hacks 答案的评论,答案是否定的。
  • 所有元素都是这种格式吗?就像三个字母和一个冒号,然后是一个字母和一个数字?您需要更具体地了解您的输入值。
  • 不...格式是:

标签: java string collections


【解决方案1】:

您可以使用带有String.CASE_INSENSITIVE_ORDER 标志集的TreeSet

    String startingString = "ABC:123,abc:123";
    List<String> caseSensitiveList = Arrays.asList(startingString.split(","));
    Set<String> caseInsensitiveSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    caseInsensitiveSet.addAll(caseSensitiveList);

    for(String caseInsensitiveString : caseInsensitiveSet){
        System.out.println(caseInsensitiveString);
    }

这段代码在运行时会给出输出:

ABC:123

【讨论】:

  • 如果问题根本不涉及排序,我不会使用TreeSet
  • @ThiagoPorciúncula 据我所知,您需要使用 TreeSet 来使用该覆盖。
  • @MikeElofson 不,你没有。您可以使用HashSet,首先将所有元素设为大写,或者您可以编写一个包装字符串并在equals 和hashCode 方法中忽略大小写的新类。在不需要时使用TreeSet 对性能不利,因为所有基本操作的时间复杂度都是O(log n) 而不是O(1)
  • @MikeElofson 我明白这一点。这就是为什么我会采用不同的解决方案。您使用TreeSet 是因为这个小细节,而不是因为TreeSet 实际上是什么。使用此解决方案,OP 将获得更慢且(无用)排序的 Set,而不是他原来的 HashSet
  • 这是一个很好的开箱即用解决方案。在每次插入之前强制客户端执行 toUpperCase 可能更有效,但也更容易出错。您可以在自己的数据结构中包装一个 HashSet,以确保在插入时完成此操作,但您要么必须 A)扩展 HashSet(并注意覆盖许多方法以维护不变量)或 B)包装一个 HashSet而且还要写很多转发方法。在对应用程序进行基准测试并且确定 O(log n) 构成瓶颈之前,我认为基于 TreeSet 的单线是可取的。
【解决方案2】:

替换

memberList.split(",")

memberList.toUpperCase().split(",")

【讨论】:

  • 这将转换 ABC:123a4,abc:1234A4 以及相同的元素。我只想要第一部分,即之前:不区分大小写
  • @Bidisha 然后将这个关键细节添加到您的问题中。
【解决方案3】:

最干净的解决方案是@SQLHacks 建议的解决方案。但后来你说ABC:123a4 必须不同于abc:1234A4。我想现在唯一的解决方案是为 String 对象创建一个包装器并覆盖 equals()hashCode() 方法来做你想做的事,正如@PaulBoddington 在他的评论中所建议的那样。


这是我想出的(根据@nafas 的回答进行了编辑和改进):

public class StringWrapper {

    private String value;

    private String beforeColon;
    private String afterColon;

    private int hash;

    public StringWrapper(String value) {
        this.value = value;

        String[] splitted = value.split(":");
        beforeColon = splitted[0];
        afterColon = splitted[1];
        hash = Objects.hash(beforeColon.toUpperCase(), afterColon);
    }

    public String getValue() {
        return value;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof StringWrapper) {
            StringWrapper other = (StringWrapper) obj;
            return beforeColon.equalsIgnoreCase(other.beforeColon) && afterColon.equals(other.afterColon);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return hash;
    }

    @Override
    public String toString() {
        return value;
    }

}

然后:

    // this method is just to help you building a List<StringWrapper> from your String (memberList variable)
    public static List<StringWrapper> split(String string, String regex) {
        List<StringWrapper> list = new ArrayList<>();
        for (String element : string.split(regex)) {
            list.add(new StringWrapper(element));
        }
        return list;
    }

    public static void main(String[] args) {
        String memberList = "ABC:123,abc:123,ABC:123a4,ABC:123A4";

        List<StringWrapper> memlist = new ArrayList<>(split(memberList, ","));
        Set<StringWrapper> memberSet = new HashSet<>(memlist);
        memlist = new ArrayList<StringWrapper>(memberSet);

        for (StringWrapper element : memlist) {
            System.out.println(element);
        }
    }

如果你运行它,你会得到以下输出:

ABC:123a4
ABC:123A4
ABC:123

abc:123 不在,但 ABC:123a4ABC:123A4 都在。

您可以更轻松地更改静态split 方法来为您创建Set。我没有这样做的原因是为了让你看起来很熟悉。

【讨论】:

    【解决方案4】:

    实际上创建一个小的Model 类来处理所有可能的情况有什么问题?

    final class Model{
     final String firstPart;
     final String secondPart;
     final int hashCode;
      Model(String s){
       String[] splitted=s.split(":");
       firstPart=splitted[0];
       secondPart=splitted[1];
       hashCode=Objects.hash(firstPart.toLowerCase(),secondPart);
    
      }
    
      @Override
      public boolean equals(Object o){
       String[] splitted=o.toString().split(":");
       return firstPart.equalsIgnoreCase(splitted[0]) && secondPard.equals(splitted[1]);
      }
    
      @Override
      public int hashCode(){
       return hashCode;
      }
    
      @Override
      public String toString(){
        return firstPart+":"+secondPart;
      }
    }
    

    现在创建一个集合等等:

    Set<Model> set =new HashSet<Model>();
    

    【讨论】:

    • Model 的想法与包装器相同。不过,您的 equals()hashCode() 实现看起来更干净。
    • @ThiagoPorciúncula 你是对的伙伴,抱歉我没有详细介绍你的实现。
    【解决方案5】:

    你可以试试这个 Java 8 one liner

    Set<String> memberSet = memlist.stream().map(s -> s.toUpperCase()).collect(Collectors.toSet());
    

    它将遍历 memlist 中的所有字符串,将它们转换为大写并将它们放入一个集合中。

    这当然意味着如果您的列表包含“abc:123”但不包含“ABC:123”,您仍然会在集合中获得“ABC:123”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2021-09-17
      • 2012-08-12
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 2018-09-06
      相关资源
      最近更新 更多