ArrayList包含区分大小写


67

我目前正在使用属于ArrayList类的contains方法进行搜索。有没有办法使此搜索在Java中不区分大小写?我发现在C#中可以使用OrdinalIgnoreCase。是否有等效的Java或另一种方式来做到这一点?谢谢。


您是否需要维护列表中字符串的大小写,还是可以全部将它们都小写?
亚伦·J·朗

我需要保持相同的情况。
约翰

如果在原始情况下也需要返回String,则contains()仅有助于指示它在列表中。HashMap可以在给定大写或小写字符串的情况下检索原始字符串,并且具有更好的搜索特性(但不会保留字符串的原始顺序)。
Maarten Bodewes 2012年

感谢大家的投入,现在我需要决定要使用哪种解决方案。再次感谢。
2012年

这些年来,我简直不敢相信Java没有做到这一点的简便方法。
Brain2000

Answers:


65

您可以像使用其他任何ArrayList一样使用它。您可以将该列表传递给其他代码,并且外部代码不必了解任何字符串包装器类。

public class CustomStringList3 extends ArrayList<String> {
    @Override
    public boolean contains(Object o) {
        String paramStr = (String)o;
        for (String s : this) {
            if (paramStr.equalsIgnoreCase(s)) return true;
        }
        return false;
    }
}

6
这假定插入列表后不需要原始大小写。这是可能的。这取决于OP的用例。或者,并行维护第二个列表。一个使用原始数据,第二个仅使用小写。
rfeak 2012年

2
@rfeak非常正确!Kinda不好意思我错过了。事后看来,我将使用带有小写字母键和原始字符串值的地图
Aaron J Lang

2
现在只是集思广益以娱乐,我相信我们为OP提供了不错的选择。您可以像这样子类化,但是可以替代over()覆盖add()和get()来使用equalsIgnoreCase()吗?理想的情况是,使用相同的存储空间,保留原始String的内容/顺序,保留String参数(相对于我的想法),然后简单地实例化其他List实现。
rfeak 2012年

1
@rfeak天才!我现在
就送

1
亚伦,如果您对同一问题有不同的答案:请将它们作为单独的答案发布。这使得讨论变得不可能。最后的评论完全是关于Map解决方案的。不要害怕错误的答案,只需将其标记为这样,用户就可以从可能的解决方案列表中剔除它们。
Maarten Bodewes,2012年

49

在Java8中,使用anyMatch

List<String> list = Arrays.asList("XYZ", "ABC");
String matchingText = "xYz";

boolean isMatched = list.stream().anyMatch(matchingText::equalsIgnoreCase);

注:stream()anyMatch()需要API 24
Mangesh

34

如果您使用的是Java 8,请尝试:

List<String> list = ...;
String searchStr = ...;
boolean containsSearchStr = list.stream().filter(s -> s.equalsIgnoreCase(searchStr)).findFirst().isPresent();

18
我知道它很旧,但是您也可以使用anyMatch()代替filter()。findFirst()。isPresent()
RudyVerboven

作为主要的Android开发人员,我仍然倾向于避免使用Java 8特定的API,而更喜欢 stackoverflow.com/a/8751546/1815624
CrandellWS


14

传统上,您可以开发自己的逻辑来比较持有的字符串ArrayList。可能有以下几种方式可以做到这一点。

public boolean containsCaseInsensitive(String strToCompare, ArrayList<String>list)
{
    for(String str:list)
    {
        if(str.equalsIgnoreCase(strToCompare))
        {
            return(true);
        }
    }
    return(false);
}

为什么不应该使用一些直接且方便的方式,例如不区分大小写的比较器使用如下所示的SortedSet ?

Set<String> a = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

a.add("A");
a.add("B");
a.add("C");


Set<String> b = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

b.add("a");
b.add("b");
b.add("c");

System.out.println(b.equals(a));

true在这种特殊情况下,将比较两个不同的集合而不考虑case和return ,并且您的比较将毫无问题地进行。


9

看一下Java API,没有包含这种方法。

但是您至少可以做两件事:

  1. 用您自己的或equalsIgnoreCase(str)覆盖ArrayList对象中的equals方法
  2. 编写您自己的contains方法,该方法应遍历ArrayList实体,并进行手动检查。

    ArrayList<String> list = new ArrayList<String>();
    ...
    containsIgnoreCase("a", list);
    
    public boolean containsIgnoreCase(String str, ArrayList<String> list){
        for(String i : list){
            if(i.equalsIgnoreCase(str))
                return true;
        }
        return false;
    }
    

1
有时候,简单的答案是最好的,感谢您的输入和欢迎,并提出了建议。
Maarten Bodewes 2012年

这类似于对列表进行相同操作的答案stackoverflow.com/a/24829584/1815624
CrandellWS

7

假设您有一个 ArrayList<String>

我能想到的唯一方法是在String周围创建一个非常轻巧的包装类,并覆盖equals和hashcode以忽略大小写,equalsIgnoreCase()并尽可能利用。然后,您将拥有一个ArrayList<YourString>。不过,这有点丑陋。


3
+1,但我不同意您的最后一句话:对于该应用程序而言,这几乎不是一个丑陋的解决方案。
Zéychin

@AaronJLang-我评论了您的答案。这取决于是否需要原始案例。是的,这可能会更容易。取决于OP的用例。
rfeak 2012年

1
@Zéychin-嘿 在我自己编写代码之前,我先去Guava或Commons Lang寻找一个更好的解决方案,这很丑陋。:)
rfeak 2012年

5
不想成为一个失败者,但是扩展列表是否比扩展/包装所有字符串更简单/更高效/更兼容?
亚伦·朗

@AaronJLang-我认为这两种解决方案都可以完成工作。这取决于他们最满意的OP。正如我最初表达的那样,我对自己的解决方案甚至都不满意:)
rfeak 2012年

4

您可以使用collections4(apache)中的IterableUtils和Predicate。

List<String> pformats= Arrays.asList("Test","tEst2","tEsT3","TST4");

Predicate<String> predicate  = (s) -> StringUtils.equalsIgnoreCase(s, "TEST");
if(IterableUtils.matchesAny(pformats, predicate)) {
    // do stuff
}

IterableUtils(collections4):org.apache.commons.collections4.IterableUtils.html


2

contains方法基于equals您的ArrayList退货中存储的对象的方法。因此,可以,如果您使用的对象equals使用不区分大小写的比较。

因此,您可以例如使用此类(代码可能仍包含一些错字)

public class CaseInsensitiveString{
  private final String contents;

  public CaseInsensitiveString( String contents){ this.contents = contents; }

  public boolean equals( Object o ){
    return o != null && o.getClass() == getClass() && o.contents.equalsIgnoreCase( contents);
  }

  public int hashCode(){
    return o.toUpperCase().hashCode();
  }
}

所有charSequences(即“不区分大小写”有意义的任何东西)都具有区分大小写的equals()方法,不是吗?
亚伦·J·朗

1
也许添加吸气剂而o.equalsIgnoreCase(contents)不是使用o.toUpperCase().equals(contents.toUpperCase())equalsIgnoreCase()可以更好地进行优化,并且可能不需要为两个大写字符串分配对象。
Maarten Bodewes 2012年

@owlstead可以肯定,使用吸气剂会很好,但不需要说明我的观点。这equalsIgnoreCase确实是一种改进,我将相应地编辑我的答复
罗宾(Robin)2012年

2

ArrayList的contains()方法通过在您提供的对象上调用equals()方法来检查是否相等(而不是数组中的对象)。因此,一种有点怪异的方法是围绕String对象创建一个包装器类,如下所示:

class InsensitiveStringComparable {
    private final String val;

    public InsensitiveStringComparable(String val) {
        this.val = val;
    }

    @Override
    public boolean equals(Object x) {
        if (x == this)
            return true;
        else if (x == null)
            return false;
        else if (x instanceof InsensitiveStringComparable)
            return ((InsensitiveStringComparable) x).val.equalsIgnoreCase(val);
        else if (x instanceof String)
            /* Asymmetric equals condition */
            return val.equalsIgnoreCase((String) x);
        else
            return false;
    }

    @Override
    public int hashCode() {
        return val.toUpperCase().hashCode();
    }
}

然后,您可以使用它来执行测试。示例“手动”测试用例:

public class Test {
    public static void main(String[] args) {
        ArrayList<Object> a = new ArrayList<Object>();
        a.add("test");
        System.out.println(a.contains(new InsensitiveStringComparable("TEST")));
        System.out.println(a.contains(new InsensitiveStringComparable("tEsT")));
        System.out.println(a.contains(new InsensitiveStringComparable("different")));
    }
}

3
如果覆盖等于,则应覆盖hashCode。
罗格尔·加西亚

@rogelware是的,尽管如果您仅将InsensitiveStringComparable用作内部“抛出”类,专门用于进行不区分大小写的ArrayList搜索,则不需要覆盖hashCode,因为ArrayList不使用它(如果原本应该这样做,则是另一回事(例如,与HashMap一起使用)
Seramme'1

如果列表使用字符串与InsensitiveStringComparable进行比较而不是相反,则将不起作用。
罗格尔·加西亚

1
@Seramme可能是正确的,但是非常明显的优化方法是将对象放入HashSet或HashMap中,从而破坏代码。许多静态验证器(例如CheckStyle和FindBugs)会很乐意将其标记为问题。最重要的是,将来可以重写contains()方法,以在equals()之前测试hashCode()的相等性,没有什么反对的。只是不要去那里。
Maarten Bodewes 2012年

@owlstead是的,非常有效的参数。我已经编辑了原始答案以反映这一点。
Seramme 2012年

2

另一个解决方案:

public class IgnorecaseList extends ArrayList<String>{

    @Override
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    } 

    @Override
    public int indexOf(Object o) {
        if(o instanceof String){
            for (int i = 0; i < this.size(); i++) {
                if(((String)o).equalsIgnoreCase(get(i))){
                    return i;
                }
            }
        }
        return -1;
    }
}

contains()方法使用indexOf ...在这种解决方案中,您还可以知道字符串在何处。list.add("a")->list.indexOf("A") == 0list.indexOf("a") == 0..

您还应该考虑使用Set而不是List。


contains()在API中未指定它使用indexOf(),因此您不能为此使用它。如果采用contains()和indexOf()的方式,则您可能应该覆盖两者,并希望不会定义其他任何需要兼容性的方法。我认为,这将其排除为可靠的解决方案。
Maarten Bodewes 2012年

2

对于java8

list.stream()。anyMatch(s-> s.equalsIgnoreCase(yourString))

对于<java8

  • 正如上述Aaron J Lang所建议
  • 或者,如果您知道列表的大小写(全部大写/全部小写),则在比较之前将搜索字符串转换为适当的大小写

2

就我而言,由于ArrayList中的所有字符串均为小写形式,因此我只执行String。contains()参数上的toLowerCase()方法。像这样:

If (yourArrayList.contains (parameterInput.toLowerCase()) {
    // your code here
}

如您所见,如果arrayList具有upperCase字符串,则可以做相反的事情:

If (yourArrayList.contains (parameterInput.toUpperCase ()) {
    // your code here
}

使用这种方法,您无需覆盖任何内容。当您的arrayList包含大小写混合时,例外。


2

我会这样:

public boolean isStringInList(final List<String> myList, final String stringToFind) {
    return myList.stream().anyMatch(s -> s.equalsIgnoreCase(stringToFind));
}  

1

不需要附加功能,可以通过将比较的字符串转换为大写或小写字母来实现所需的结果

(我知道这已在评论中提出,但并未提供完整答案)

例如:根据JTextField提供的输入,在过滤JList内容时忽略大小写:

private ArrayList<String> getFilteredUsers(String filter, ArrayList<User> users) {
    ArrayList<String> filterUsers = new ArrayList<>();
    users.stream().filter((user) -> (user.getUsername().toUpperCase().contains(filter.toUpperCase()))).forEach((user)-> {
        filterUsers.add(user.getUsername());
    });
    this.userList.setListData(filterUsers.toArray());
    return filterUsers;
    /** 
     *  I see the redundancy in returning the object... so even though,
     *  it is passed by reference you could return type void; but because
     *  it's being passed by reference, it's a relatively inexpensive
     *  operation, so providing convenient access with redundancy is just a 
     *  courtesy, much like putting the seat back down. Then, the next
     *  developer with the unlucky assignment of using your code doesn't 
     *  get the proverbially dreaded "wet" seat.
     */
}

1

不要重新发明轮子。使用经过良好测试的API。为了您的目的,请使用Apache Commons StringUtils

从Javadoc: 将给定的字符串与searchStrings的CharSequences变量比较,如果该字符串等于任何searchStrings,则返回true,而忽略大小写。

import org.apache.commons.lang3.StringUtils;
...
StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
StringUtils.equalsAnyIgnoreCase(null, null, null)    = true
StringUtils.equalsAnyIgnoreCase(null, "abc", "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", null, "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true

如何将其用于List <String>?
kio21 '19

1
@ kio21您必须将列表转换为数组:String [] arr = list.toArray(new String [list.size()]); 将数组作为第二个参数传递。
Cengiz

1

第一种方式

  List<String> list = Arrays.asList("XYZ", "ABC");
    String matchingText = "XYZ1";
        boolean isMatched = list.stream().anyMatch(matchingText::equalsIgnoreCase);
        System.out.println(isMatched);

第二路

List<String> list1= Arrays.asList("XYZ", "ABC");
String searchStr = "abC";
boolean containsSearchStr = list1.stream().filter(searchStr::equalsIgnoreCase).findFirst().isPresent();
System.out.println(containsSearchStr);

0

这是将列表项转换为小写字母的最佳方法。转换后,您将使用包含方法。喜欢

List<String> name_list = new ArrayList<>();
name_list.add("A");
name_list.add("B");

使用上面创建的创建小写列表 name_list

List<String> name_lowercase_list = new ArrayList<>();
for(int i =0 ; i<name_list.size(); i++){
 name_lowercase_list.add(name_list.get(i).toLowerCase().toString()); 
}

for(int i =0 ; i<name_list.size(); i++){
  String lower_case_name =  name_list.get(i).toLowerCase().toString();
  if(name_list.get(i).contains(your compare item) ||
   name_lowercase_list.get(i).contains(your compare item) ){
     //this will return true
   }
}

0

如果您不想创建新函数,可以尝试以下方法:

List<String> myList = new ArrayList<String>(); //The list you're checking
String wordToFind = "Test"; //or scan.nextLine() or whatever you're checking

//If your list has mix of uppercase and lowercase letters letters create a copy...
List<String> copyList = new ArrayList<String>();
for(String copyWord : myList){
   copyList.add(copyWord.toLowerCase());
}

for(String myWord : copyList){
   if(copyList.contains(wordToFind.toLowerCase()){
      //do something
   }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.