Constructing a 4×4 grid of words I have an ArrayList which contains Strings with a size of 1000. I am looping through that ArrayList and finding words and trying to form a square from them. For example as follows: 

C A R D
A R E A
R E A R
D A R T


The square must match left to right and top to bottom. So first left to right word (card) must match first top to bottom (card) and so on. The list has [voes, rose, soon, odds] and so on, a 1000 of them all of length 4. 

I am looping through them 1 by 1. So take 'voes' first, loop through remaining 999, find the first word starting with 'o', i store it as second word (example: odds) and move on to next loop. 

This will give me for example: 

voes
odds


loop 999 times again. first letter to start with 'e' and its 3rd letter is 'd' store that as 3rd name and move on to 4th loop. 

This will give me for example: 

voes
odds
eddo


loop 999 times again. Similar check for 4th word. And lets say I cant find any word that meets the condition. Break out of loop, go on top and replace the first word to the second word in the list and do it all over again.   

I have managed to get it working but it involves a lot of nested looping. If I am reading this right, this is O(n^4). Is there a more efficient way to do this.  

void makeWord(ArrayList&lt;String&gt; arr){

    String first, second, third, fourth;
    for (int i = 0; i &lt; arr.size(); i++) {

        first = arr.get(i);

        for (int j = 0; j &lt; arr.size(); j++) {
            if(first.substring(1,2).equals(arr.get(j).substring(0,1)) &amp;&amp;
                    (!first.equals(arr.get(j)))){
                second = arr.get(j);
            }
            else {
                second = "    ";
            }

            if (!second.trim().isEmpty()) {
                for (int k = 0; k &lt; arr.size(); k++) {
                    if (first.substring(2, 3).equals(arr.get(k).substring(0, 1)) &amp;&amp;
                            second.substring(2, 3).equals(arr.get(k).substring(1, 2)) &amp;&amp;
                            (!first.equals(arr.get(k)) &amp;&amp; (!second.equals(arr.get(k))))) {
                        third = arr.get(k);
                    } else {
                        third = "    ";
                    }

                    if (!third.trim().isEmpty()) {

                        for (int m = 0; m &lt; arr.size(); m++) {
                            if(first.substring(3,4).equals(arr.get(m).substring(0,1)) &amp;&amp;
                                    second.substring(3,4).equals(arr.get(m).substring(1,2))&amp;&amp;
                                    third.substring(3,4).equals(arr.get(m).substring(2,3)) &amp;&amp;
                                    (!first.equals(arr.get(m)) &amp;&amp; (!second.equals(arr.get(m))))){
                                fourth = arr.get(m);
                            }
                            else {
                                fourth = "    ";
                            }

                            if (!(fourth.trim().isEmpty())) {
                                System.out.println(first);
                                System.out.println(second);
                                System.out.println(third);
                                System.out.println(fourth);
                                System.out.println(" ");
                            }
                        }
                    }
                }
            }
        }
    }
}
I'm not sure if there's a faster way of doing it. However, your current code can be improved.
Enhanced for loop
Your loop are written using the traditional index:
String first;
for (int i = 0; i &lt; arr.size(); i++) {
    first = arr.get(i);
    // ...

When you don't need the index, it is better to use the enhanced for loop. This has the advantage that it makes the code more compact and easier to read:
for (String first : arr) {
     // ...

You are also calling multiple times arr.get(j) with the index instead of storing the result in a local variable. Using the enhanced for loop solves that directly.
String comparisons
Right now, you are comparing each String together with
if(first.substring(1,2).equals(arr.get(j).substring(0,1)) &amp;&amp;
    (!first.equals(arr.get(j)))){

There are a couple of comments here:

This is very hard to read: it is unclear what the code is doing ;
This is split in two lines and has a lot of parentheses which doesn't help
Each line is &quot;compact&quot; so it's hard to see what is actually happening.

To rewrite that, we should remember that what we're trying to do here: test whether the 2nd character of the 1st string is the same as the 1st character of the 2nd string. So, instead of using substring, let's use charAt which directly returns the character at the given index. The 2nd character of the first String is first.charAt(1); the 1st charcter of the second String is second.charAt(0). As such, we could have:
if (!first.equals(second) &amp;&amp; first.charAt(1) == second.charAt(0)) {
    // ...
}

Note the added spaces before the curly braces, between the operators and after the if statement, it also contributes to clarity.
Bizarre default condition
When the word currently considered is not a property fit, you are dping
else {
    second = &quot;    &quot;;
}

if (!second.trim().isEmpty()) {
    // ...
}

You are explicitely setting the String to whitespaces, only to trim it afterwards. This is a bit awkward.
What you want to do here is to continue when you reach an unwanted word. So you could have instead:
for (String first : arr) {
    for (String second : arr) {
        if (second.equals(first) || second.charAt(0) != first.charAt(1)) {
            continue;
        }
        for (String third : arr) {
            if (third.equals(first) || third.equals(second) || 
                first.charAt(2) != third.charAt(0) || second.charAt(2) != third.charAt(1)) {
                continue;
            }
            for (String fourth : arr) {
                if (fourth.equals(first) || fourth.equals(second) || fourth.equals(third) ||
                    first.charAt(3) != fourth.charAt(0) || second.charAt(3) != fourth.charAt(1) || third.charAt(3) != fourth.charAt(2)) {
                    continue;
                }

but now we're back to a mess: this is hard to read. However, it gives us insight into how can we do it better.
What we want is the following: we have a list of already found words (first and second for example); with that, we want to continue when the third is incorrect. What makes it incorrect is:

either it is equal to one of the previous found word
its characters do not match with the previously found words

So we can make a method of that!
Factoring common logic
private static boolean isIncorrect(String word, String... others) {
    boolean alreadyFound = Arrays.stream(others).anyMatch(word::equals);
    boolean notMatchedChars = 
        IntStream.range(0, others.length)
                 .anyMatch(i -&gt; word.charAt(i) != others[i].charAt(others.length));
    return alreadyFound || notMatchedChars;
}

This method is split in two (and uses Java 8 idioms but the logic would be the same without it). It takes as parameter a word to test and a vararg of the currently found words.

The first test checks whether any of the currently found words others are equal to the tested word.
The second test checks whether the characters are correct. If we have 3 currently found words then

the 1st character of the word must be equal to the 3rd character of the first word;
the 2nd character of the word must be equal to the 3rd character of the second word;
the 3rd character of the word must be equal to the 3rd character of the third word;



With that method, we can refactor our code to
void makeWord(List&lt;String&gt; arr) {
    for (String first : arr) {
        for (String second : arr) {
            if (isIncorrect(second, first)) {
                continue;
            }
            for (String third : arr) {
                if (isIncorrect(third, first, second)) {
                    continue;
                }
                for (String fourth : arr) {
                    if (isIncorrect(fourth, first, second, third)) {
                        continue;
                    }
                    System.out.println(first);
                    System.out.println(second);
                    System.out.println(third);
                    System.out.println(fourth);
                    System.out.println(&quot; &quot;);
                }
            }
        }
    }
}

which is very clean, although surely O(n4).