13

I am trying to check if a string contains all the letters of the alphabet. I created an ArrayList which contains the whole alphabet. I converted the string to char array and I'm iterating through the character array, and for every character present in the ArrayList I'm removing an element from it. And in the end, I'm trying to check if the Arraylist is empty to see if all elements have been removed. That would indicate the string contains all the letters of the alphabet.

Unfortunately, the code is throwing IndexOutOfBoundsException error inside the if condition where I'm removing elements from the arraylist

List<Character> alphabets = new ArrayList<Character>();

alphabets.add('a');
alphabets.add('b');
alphabets.add('c');
alphabets.add('d');
alphabets.add('e');
alphabets.add('f');
alphabets.add('g');
alphabets.add('h');
alphabets.add('i');
alphabets.add('j');
alphabets.add('k');
alphabets.add('l');
alphabets.add('m');
alphabets.add('n');
alphabets.add('o');
alphabets.add('p');
alphabets.add('q');
alphabets.add('r');
alphabets.add('s');
alphabets.add('t');
alphabets.add('u');
alphabets.add('v');
alphabets.add('w');
alphabets.add('x');
alphabets.add('y');
alphabets.add('z');

// This is the string- I've just put a random example
String str = "a dog is running crazily on the ground who doesn't care about the world";

//Remove all the spaces
str = str.replace(" ", "");

// Convert the string to character array
char[] strChar = str.toCharArray();

for (int i = 0; i < strChar.length; i++) {

    char inp = strChar[i];

    if (alphabets.contains(inp)) {
        alphabets.remove(inp);
    }
}

if (alphabets.isEmpty())
    System.out.println("String contains all alphabets");
else
    System.out.println("String DOESN'T contains all alphabets");
Szymon
  • 168
  • 9
Siddharth
  • 189
  • 1
  • 1
  • 8
  • 5
    instead of doing a `List` you could just loop as `int i = (int)'a'; i <= 'z'` – SomeJavaGuy Sep 01 '16 at 06:04
  • 11
    All the [alphabets](https://en.wikipedia.org/wiki/List_of_writing_systems#True_alphabets)? Like Greek (αβγδε...ψω) and Russian (абвгд...юя)? Or did you mean "all *letters* of the (aka English) alphabet" (abcde...yz)? – Andreas Sep 01 '16 at 06:15
  • Related: [Efficient Java language constructs to check if string is pangram?](http://stackoverflow.com/q/37880301/2513200) – Hulk Sep 01 '16 at 07:37
  • 1
    @KevinEsche: there's also "abc...xyz".toCharArray() if the list contains characters that do not follow each other – Thomas Weller Sep 01 '16 at 08:05
  • Just do it the other way around, loop alphabets and check if each letters are inside your string. – the_lotus Sep 01 '16 at 13:36
  • A shorter way to create the list would be Arrays.asList("abcdefghijklmnopqrstuvwxyz".toArray()) – Phoenix Sep 01 '16 at 17:50
  • Related codegolf challenge: [Is it a pangram?](http://codegolf.stackexchange.com/q/66197/4372) – daniero Sep 01 '16 at 20:55
  • new Set("".toLowerCase().replace(/[^a-z]/gi, "").split("")).size === 26 It returns true or false for the given specifying the string contains all the letters of the alphabet or not. – rockey91 Mar 23 '19 at 06:51

16 Answers16

76

All these solutions seem to do a lot of work for a relatively simple check, especially given Java 8's stream API:

/* Your lowercase string */.chars()
    .filter(i -> i >= 'a' && i <= 'z')
    .distinct().count() == 26;

Edit: For speed

If you want to end the string iteration as soon as the entire alphabet is found while still using streams, then you can keep track with a HashSet internally:

Set<Integer> chars = new HashSet<>();
String s = /* Your lowercase string */;
s.length() > 25 && s.chars()
    .filter(i -> i >= 'a' && i <= 'z') //only alphabet
    .filter(chars::add)                //add to our tracking set if we reach this point
    .filter(i -> chars.size() == 26)   //filter the 26th letter found
    .findAny().isPresent();            //if the 26th is found, return

This way, the stream will cease as soon as the Set is filled with the 26 required characters.

There are some (even still) more efficient solutions in terms of performance below, but as a personal note I will say to not bog yourself in premature optimization too much, where you could have readability and less effort in writing the actual code.

Rogue
  • 11,105
  • 5
  • 45
  • 71
  • 2
    +1 elegant solution for English. But the filter quickly gets a lot more complicated for other languages (as do the definitions of what "pangram" means - how to treat accented versions of letters, etc.) – Hulk Sep 01 '16 at 07:28
  • 1
    True, however this is quite a bit mutable in that you can substitute different filters and alphabet sizes. You could use an enum for that, even. :) – Rogue Sep 01 '16 at 12:02
  • I'm still new to Java 8 and thus I have a question. What is a BigO for this method chaining solution? – hahn Sep 01 '16 at 15:41
  • 7
    The problem with this is it can take a long time for long strings - it will process the whole string, even if the a small first part of the string contains all the letters. Consider a string who's first 26 letters are the alphabet, followed by 1 million other characters. A smart solution would stop searching after the 1st 26 characters. – iobender Sep 01 '16 at 16:19
  • 1
    Some JMH tests comparing vanilla java(implementation from @hahn ) with streams implementation above(AVGT,CNT 200): streams: 1.118 ± 0.012 ns/op; vanilla: 0.470 ± 0.009 ns/op – oborovyk Sep 01 '16 at 18:42
  • @iobender when I get home I believe there's a way around that whilst still using streams, will update – Rogue Sep 01 '16 at 19:53
  • 1
    @iobender updated my answer, it's a little messier I suppose but the speed benefit is there at least – Rogue Sep 01 '16 at 21:01
  • Sorry, but for me, the second soluttion looks like a **horrible** (and dangerously brittle) abuse of the `filter` method.... – Marco13 Sep 02 '16 at 16:13
  • @Marco13 I wouldn't recommend it for a parallel stream, but then again I wouldn't recommend a parallel stream for parsing a string. – Rogue Sep 02 '16 at 16:44
16

List.remove removes by index. Since a char can be cast to an int you are effectively removing index values that do not exist, ie char 'a' is equal to int 97. As you can see your list does not have 97 entries.

You can do alphabet.remove(alphabets.indexOf(inp));

As pointed out by @Scary Wombat(https://stackoverflow.com/a/39263836/1226744) and @Kevin Esche (https://stackoverflow.com/a/39263917/1226744), there are better alternative to your algorithm

Community
  • 1
  • 1
Leon
  • 12,013
  • 5
  • 36
  • 59
  • 2
    The asker could (and should) also use `List list` and use a boxed `Character` when you get the character from the String `Character inp = strChar[i];` With that he avoids calling the wrong `remove` method, too. – Florian Link Sep 01 '16 at 06:12
  • Thanks for that. I used Hashset instead of ArrayList and it worked too. – Siddharth Sep 01 '16 at 06:23
9

O(n) solution

static Set<Integer> alphabet = new HashSet<>(26);

public static void main(String[] args) {

    int cnt = 0;

    String str = "a dog is running crazily on the ground who doesn't care about the world";

    for (char c : str.toCharArray()) {
        int n = c - 'a';
        if (n >= 0 && n < 26) {
            if (alphabet.add(n)) {
                cnt += 1;
                if (cnt == 26) {
                    System.out.println("found all letters");
                    break;
                }
            }
        }
    }
}
hahn
  • 3,588
  • 20
  • 31
8

Regex is your friend. No need to use a List here.

public static void main(String[] args) {
    String s = "a dog is running crazily on the ground who doesn't care about the world";
    s = s.replaceAll("[^a-zA-Z]", ""); // replace everything that is not between A-Za-z 
    s = s.toLowerCase();
    s = s.replaceAll("(.)(?=.*\\1)", ""); // replace duplicate characters.
    System.out.println(s);
    System.out.println(s.length()); // 18 : So, Nope

    s = "a dog is running crazily on the ground who doesn't care about the world qwertyuioplkjhgfdsazxcvbnm";
    s = s.replaceAll("[^a-zA-Z]", "");
    s = s.toLowerCase();        
    s = s.replaceAll("(.)(?=.*\\1)", "");
    System.out.println(s);
    System.out.println(s.length()); //26 (check last part added to String)  So, Yes

}
TheLostMind
  • 35,966
  • 12
  • 68
  • 104
  • That's a cool answer but it doesn't actually answer why the code was crashing – Pierre Arlaud Sep 01 '16 at 13:13
  • 1
    @PierreArlaud - You are right. It doesn't answer why the OP's code is crashing. But, answering a question by asking the OP to use a different approach is fine (because in the future other people could come here and might be thankful for all the "new approaches") – TheLostMind Sep 01 '16 at 13:16
  • Since we're at it, do you think this solution will – performance-wise – outrun the approaches using lists? – Pierre Arlaud Sep 01 '16 at 13:21
  • 1
    @PierreArlaud - Well, this one's performance can be improved by using `Pattern` and `Matcher` instead of direct `String#replaceAll()` . But no, this answer will not be faster than a `List` / `Map` approach where the complexity would be `O(n)` – TheLostMind Sep 01 '16 at 13:26
  • 1
    If the OP code is crashing even after trying to debug it himself, it means the OP has a missunderstanding about the statements in his code. Since all of them is quite generic (it is not an unknown third party API crashing), if the OP does not learn why his code crashed he is likely to have the same problem again. – SJuan76 Sep 01 '16 at 14:44
8

Adding to @Leon answer, creating a List and removing from it seems quite unnecessary. You could simply loop over 'a' - 'z' and do a check with each char. Additionally you are looping over the whole String to find out, if each letter is present. But the better version would be to loop over each letter itself. This can potentionally safe you a few iterations.

In the end a simple example could look like this:

// This is the string- I've just put a random example
String str = "a dog is running crazily on the ground who doesn't care about the world";
str = str.toLowerCase();

boolean success = true;
for(char c = 'a';c <= 'z'; ++c) {
    if(!str.contains(String.valueOf(c))) {
        success = false;
        break;
    }
}

if (success)
    System.out.println("String contains all alphabets");
else
    System.out.println("String DOESN'T contains all alphabets");
SomeJavaGuy
  • 7,307
  • 2
  • 21
  • 33
5

Another answer has already pointed out the reason for exception. You have misused List.remove(), as it implicitly convert char to int which it called the List.remove(int) which remove by index.

The way to solve is actually easy. You can make it call the List.remove(Object) by

alphabets.remove((Character) inp);

Some other improvements:

  1. You should use Set instead of List in this case.
  2. You can even use a boolean[26] to keep track of whether an alphabet has appeared
  3. You do not need to convert your string to char array. Simply do a str.charAt(index) will give you the character at certain position.
Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
4

One integer variable is enough to store this information. You can do it like this

public static boolean check(String input) {
  int result = 0;    
  input = input.toLowerCase();
  for (int i = 0; i < input.length(); i++) {
    char c = input.charAt(i);
    if (c >= 'a' && c <= 'z') {
      result |= 1 << (input.charAt(i) - 'a');
    }
  }
  return result == 0x3ffffff;
}

Each bit corresponds to a letter in English alphabet. So if your string contains all letters the result will be of form 00000011111111111111111111111111

cliffroot
  • 1,839
  • 17
  • 27
  • 1
    This 1. Does **not** needlessly create a new array with `toCharArray` (the `toLowerCase` is optional and can easily be commented out when not required), 2. Does **not** use odd RegExes (I mean, Regex, for this, seriously?), 3. Does **not** do any O-complexity-increasing kludges with `indexOf` etc., 4. Does **not** needlessly store the (boxed!) characters in a collection. It is closest to the solution that I would have used - and thus, the only answer here that gets a +1 from me (with `1<<(c-'a')` and an early return `if (result==0x3ffffff)return true` **in** the loop it would still be better) – Marco13 Sep 01 '16 at 21:07
3

How about creating

List<String> alphabets = new ArrayList <String> ();

and add values as strings

then

for (String val : alphabets) {   // if str is long this will be more effecient
     if (str.contains (val) == false) {
        System.out.println ("FAIL");
        break;
     }
}
Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
3

You can get rid of the exception, by changing this line in your code

char inp = strChar[i];

to

Character inp = strChar[i];

Refer https://docs.oracle.com/javase/7/docs/api/java/util/List.html#remove(java.lang.Object)

List.remove('char') is treated as List.remove('int'), which is why you are getting indexOutOfBoundsException, because it is checking the ASCII value of 'a' which is 97. Converting variable 'inp' to Character would call List.remove('Object') api.

Anoop LL
  • 1,548
  • 2
  • 21
  • 32
2

And if you like Java 8 streams like me:

final List<String> alphabets = new ArrayList<>();

And after filling alphabets with a-z:

final String str = "a dog is running crazily on the ground who doesn't care about the world";
final String strAsLowercaseAndWithoutOtherChars = str.toLowerCase()
                                                     .replaceAll("[^a-z]", "");

final boolean anyCharNotFound = alphabets.parallelStream()
       .anyMatch(t -> !strAsLowercaseAndWithoutOtherChars.contains(t));

if (anyCharNotFound) {
    System.out.println("String DOESN'T contains all alphabets");
} else {
    System.out.println("String contains all alphabets");
}

This converts the string to lower case (skip if you really are only looking for the small letters), removes all characters from the string which are not small letters and then checks for all members of your alphabets if they are contained in the string by using a parallel stream.

Florian Link
  • 638
  • 4
  • 11
2

Here's another naive solution that uses String.split("") to split every character into a String[] array, then Arrays.asList() to convert that to a List<String>. You can then call yourStringAsList.containsAll(alphabet) to determine whether your String contains the alphabet:

String yourString = "the quick brown fox jumps over the lazy dog";
        
List<String> alphabet = Arrays.asList("abcdefghijklmnopqrstuvwxyz".split(""));
List<String> yourStringAsList = Arrays.asList(yourString.split(""));
        
boolean containsAllLetters = yourStringAsList.containsAll(alphabet);
        
System.out.println(containsAllLetters);

This approach might not be the fastest, but I think the code is a littler easier to understand than the solutions proposing loops and streams and whatnot.

Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
1

For Java 8, it could be written like:

boolean check(final String input) {
    final String lower = input.toLowerCase();
    return IntStream.range('a', 'z'+1).allMatch(a -> lower.indexOf(a) >= 0);
}
Laurel
  • 5,965
  • 14
  • 31
  • 57
j2ko
  • 2,479
  • 1
  • 16
  • 29
1

Just do something like

sentence.split().uniq().sort() == range('a', 'z')
Laurel
  • 5,965
  • 14
  • 31
  • 57
lblzsd
  • 11
  • 1
0
Character inp = strChar[i]; 

Use this instead of char, List remove method have 2 overloaded methods , one with object and one with int .If you pass char its been treated as the int one.

Rohit
  • 89
  • 6
0

Convert the string to lower case or capitals. Then loop thru the equivalent ascii decimal values for A-Z or a-z and return false if not found in character array. You will have to cast the int to char.

Olga
  • 31
  • 3
0

I've thought about playing with the ASCII codes of the characters.

String toCheck = yourString.toLowerCase();
int[] arr = new int[26];
for(int i = 0; i < toCheck.length(); i++) {
    int c = ((int) toCheck.charAt(i)) - 97;
    if(c >= 0 && c < 26) 
        arr[c] = arr[c] + 1;
}

After running the loop you eventually get an array of counters, each representing a letter of alphabet (index) and it's occurrence in the string.

boolean containsAlph = true;
for(int i = 0; i < 26; i++)
    if(arr[i] == 0) {
        containsAlph = false;
        break;
    }
Laurel
  • 5,965
  • 14
  • 31
  • 57