Data structures in Java. Useful helper class methods

Hi habr!

I am a Software Engineer at EPAM. For more than 8 years I have been working with legacy code written in Java (anticipating comments, I note that understanding and tolerance for legacy began long before EPAM, in conclusion you will find the answer why). Often in the work I came across the same repeated flaws. This prompted me to write a note, and I want to start with data structures and helper classes Collections and Arrays. For some reason, some developers neglect their use, and in vain

A Java developer often has to deal with various data structures. It can be arrays, all kinds of collections or implementations. Map. It would seem that everything with them is clear and understandable, but there are several little things that are easy to stumble on.

This note may be useful for both beginners who do not yet know these nuances, and experienced developers who might forget some of this.

image
Photo by ammiel jr on Unsplash

CAT

I want to make a reservation right away that this material is relevant for Java 8. It is clear that some things have already been done better in Java 9+, but most large projects most often use the version of Java 8 (and sometimes Java 6).

What is the best way to get an array based collection?

I suggest starting with the formation of a collection based on an array.

Most often, this method occurs:

Integer[] someArray = {9, 10, 11, 12};
List list = Arrays.asList (someArray); 

It certainly works, but is everything okay with it? And are there any alternative solutions?

Two minuses of this approach come to mind at once:

  • First method Arrays.aslist returns List. But what if we need a different implementation Collection? Arrays.aslist will not allow this to be done, but an alternative will be considered a little further.
  • Secondly, Listreceived as a result of a call Arrays.aslist does not support resizing. I think many have come up with an exception arising from working with such a list.

At the interface Collections you can find an alternative to the method Arrays.aslist – method Collections.addAll. Here's how to use it:

// There can be any collection (List, Set, ...)
Collection collection = ...;
Integer[] someArray = {9, 10, 8, 7};
Collections.addAll (collection, someArray); 

Or simply:

Collections.addAll (collection, 11, 12, 13, 14); 

Method Collections.addAll takes an input object Collection and an array. Instead of an array, you can also specify elements separated by commas.

What are the benefits Collections.addAllArrays.asList?

  • To begin with, Collections.addAll works much faster. This can be found in the JavaDoc of this method:

    The behavior of this convenience method is identical to that of c.addAll (Arrays.asList (elements)), but this method is likely to run significantly faster under most

  • Besides, Collections.addAll not only works with List, but also with any other collection.
  • And when using this method, there is no problem of resizing.

What's the easiest way to print an array, multidimensional array, or collection?

Let's move on to the issue of getting a printed representation of an array and collections.
If just to do System.out.println (someArray), we get something like this:
[LjavalangInteger;@6d06d69c[LjavalangInteger;@6d06d69c

A similar result is expected when using the method toString () at the array.
The method comes to the aid of the output Arrays.toString (…).

Integer[] someArray = new Integer[]{1, 2, 3};
System.out.println (Arrays.toString (someArray)); 

The output for this line will be:

[1, 2, 3] 

If we are talking about a multidimensional array, then you can use the method: Arrays.deeptoString.

int[] a = {
    {1, 2, 3},
       {4, 5, 6}
};
System.out.println (Arrays.deepToString (a)); 

The output of this snippet will be:

[[[1, 2, 3], [4, 5, 6]] 

Thus, it is not necessary to sort the array through any loop manually to display its elements, it is enough to use this method.

Regarding collections or implementations Mapthen there is no problem. All data structures except the array are normally output.

Suppose there is an example:

Collection someCollection = new HashSet <> ();
someCollection.add (1);
someCollection.add (2);
System.out.println (someCollection);

Map someMap = new HashMap <> ();
someMap.put (1, "Some 1");
someMap.put (2, "Some 2");
System.out.println (someMap); 

Note in the output below that both set and Map were displayed in a readable form:

[1, 2] 
 
{1 = Some 1, 2 = Some 2} 

How easy is it to compare arrays with each other?

There are situations when you need to compare arrays. In class Arrays there is a method that allows such a comparison. Method Arrays.equals compares the number of elements and checks the equivalence of the corresponding elements.

Let's say we have a class Elements with one field and specific equals

private class Element {
    final String name;
     private Element (String name) {this.name = name; }

     @Override
    public boolean equals (Object o) {
        if (this == o) return true;
        if (o == null || getClass ()! = o.getClass ()) return false;
        Element element = (Element) o;
        return Objects.equals (name, element.name);
    }

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

Define three arrays:

Element[] firstElementArray = {new Element ("a"), new Element ("b"), new Element ("c")};
Element[] secondElementArray = {new Element ("c"), new Element ("b"), new Element ("a")};
Element[] thirdElementArray = {new Element ("a"), new Element ("b"), new Element ("c")}; 

Please note that the first and third arrays have elements in the same order.
Now you can perform the comparison using the method Arrays.equals.

System.out.println ("first equals to second?"
        + Arrays.equals (firstElementArray, secondElementArray));
System.out.println ("second equals to third?"
        + Arrays.equals (secondElementArray, thirdElementArray));
System.out.println ("first equals to third?"
        + Arrays.equals (firstElementArray, thirdElementArray)); 

The result will be as follows:


first equals to second? false
second equals to third? false
first equals to third? true 

How to efficiently copy an array?

Often you can see in the code manually copying arrays using loops. However there is a method System.arraycopywhich will copy much faster.

I suggest a look at such a simple example:

Element[] elements = {
        new Element ("a"),
        new Element ("b"),
        new Element ("c")
};
Element[] copyOfElements = new Element[elements.length];
System.arraycopy (elements, 0, copyOfElements, 0, elements.length);
System.out.println (Arrays.toString (copyOfElements)); 

We have an array of elements. We create an empty array of the same length and copy all the elements from the first to the second. As a result, we obtain the following conclusion:

[Element{name='a'}, Element{name='b'}, Element{name='c'}] 

How to sort an array or collection in different ways?

Arrays can be sorted using the method Arrays.sort (someArray). If you want to sort the array in reverse order, then you can pass this method to the input Collections.reverseOrder () as a second parameter.

For example, there is an array that we sort in direct and then in reverse order:

String[] someArray = new String[]{"b", "a", "c"};

Arrays.sort (someArray);
System.out.println (Arrays.toString (someArray));

Arrays.sort (someArray, Collections.reverseOrder ());
System.out.println (Arrays.toString (someArray)); 

The conclusion will be as follows:

[a, b, c] 
[c, b, a] 

In addition to direct and reverse sorting, it sometimes happens that you need to sort an array of strings regardless of case. This is easy to do by passing String.CASE_INSENSITIVE_ORDER as the second parameter in Arrays.sort.

Collections.sortunfortunately, it allows you to sort only implementations List.

What algorithm sorts Java?

The last thing that can be mentioned when speaking of sorting in Java is that in Java, “simple sort” is used for the simplest types, and “stable merge” is used for objects. So you should not spend resources on developing your own implementation of the sorting method until the profiler shows that this is necessary.

What if we have an array and the method accepts Iterable?

I propose now to move on to such a question as passing an array to a method requiring Iterable. Let me remind you that Iterable Is an interface that contains a method iterator ()The Iterator should return.

If there is a method that takes Iterable at the input, then the array cannot be transferred there just like that. Despite the fact that the array can be iterated over in a loop forhe is not Iterable.

String[] someArray = new String[]{"a", "b", "c"};
for (String currentString: someArray) {
    ...
}  

In this example, everything is fine. But if there is a method:

private static void someIteration (Iterable iterable) {
    ...
} 

That line will not compile:

someIteration (someArray); 

The only way out in this situation (not counting the use of stream-ov) – convert the array into a collection and feed it to such a method already.

Briefly about a few useful Collections methods

Method Comment
max (Collection) and max (Collection, Comparator)
min (Collection) and max (Collection, Comparator)
Please note that you can apply for input Comparator
IndexOfSubList (List, List) Finds the index of the first occurrence of one list (second argument) in another (first argument)
lastIndexOfSubList (List, List) Finds the index of the last occurrence of one list (second argument) in another (first argument)
reverse (List) Reorders items in reverse order

What is worth reading?

This is only a small part of the tools that can make life easier for the developer when working with data structures. Many interesting points in the work of the collections themselves and convenient tools for working with them can be found in Bruce Eckel's book “Java Philosophy” (4th full edition). However, you should be careful, as it encounters situations that can no longer be played on Java 7, Java 8 and higher. Although Java 6 is described in this book, its material remains largely relevant today.

Of course, “Java Philosophy” should not be limited. Reading any of these books will not hurt any Java developer:

  • “Java. Effective Programming, ”Joshua Bloch.
  • “Refactoring. Improving the design of existing code, ”Martin Fowler.
  • “Clean code. Creation, analysis and refactoring ”, Robert Martin.
  • Spring 5 for Professionals, Julian Kozmin and others.
  • “Test-Driven Java Development”, Viktor Farcic, Alex Garcia (has not yet been released in Russian).

What is the result?

If you came up with interesting ideas that could complement what was written in this article, share them in the comments.

I would also like to wish good luck and patience to those who work with the legacy old code. Most major projects are legacy. And their significance for the customer is difficult to overestimate. And the feeling of victory from eliminating the bug, which took more than a week to find the causes, is not inferior to feelings at the end of the implementation of a new feature.

Thank you for attention. I would be glad if any of the presented would be useful.
All success!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *