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 the 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 of 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<Integer> 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:





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



 //      (List, Set, ...) Collection<Integer> collection = ...; Integer[] someArray = {9, 10, 8, 7}; Collections.addAll(collection, someArray);
      
      





Or simply:



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





The Collections.addAll method accepts a Collection object and an array as input. Instead of an array, you can also specify elements separated by commas.



What are the benefits of Collections.addAll over Arrays.asList ?





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 we just make System.out.println (someArray) , we get something like this:

[Ljava.lang.Integer; @ 6d06d69c.

A similar result is expected when using the toString () method on an array.

To output the array, the Arrays.toString (...) method comes to the rescue .



 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.



As for collections or implementations of Map , there are no problems. All data structures except the array are normally output.



Suppose there is an example:



 Collection<Integer> someCollection = new HashSet<>(); someCollection.add(1); someCollection.add(2); System.out.println(someCollection); Map<Integer, String> someMap = new HashMap<>(); someMap.put(1, "Some 1"); someMap.put(2, "Some 2"); System.out.println(someMap);
      
      





Note in the output below that both the set and Map were displayed in an easy-to-read format:



 [12] 
 {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. There is a method in the Arrays class that allows such a comparison. The Arrays.equals method compares the number of elements and checks the equivalence of the corresponding elements.



Let's say we have a Elements class with one field and certain 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 Arrays.equals method.



 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 System.arraycopy method that 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 Arrays.sort (someArray) method. If you want to sort the array in reverse order, you can pass Collections.reverseOrder () as the second parameter to the input to this method.



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 to Arrays.sort .



Collections.sort , unfortunately, only allows List implementations to be sorted.



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 that requires Iterable . Let me remind you that Iterable is an interface that contains the iterator () method, which Iterator should return.



If there is a method that takes Iterable at the input, then the array cannot be transferred there just like that. Although you can iterate over an array in a for loop, it 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<String> iterable) { ... }
      
      





That line will not compile:



 someIteration(someArray);
      
      





The only way out in this situation is to convert the array to a collection and already feed it to such a method.



Briefly about a few useful Collections methods



Method A comment
max (Collection) and max (Collection, Comparator)

min (Collection) and min (Collection, Comparator)

Please note that you can apply to the input of 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:





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!



All Articles