Science and technology

Dealing with maps in Groovy vs Java

Java is a good programming language, however typically I desire a Java-like language that is only a bit extra versatile and compact. That’s once I go for Groovy.

In a latest article, I reviewed a number of the variations between creating and initializing maps in Groovy and doing the identical factor in Java. In temporary, Groovy has a concise syntax for establishing maps and accessing map entries in comparison with the trouble vital in Java.

This article will delve into extra variations in map dealing with between Groovy and Java. For that goal, I’ll use the sample table of employees used for demoing the JavaScript DataTables library. To comply with alongside, begin by ensuring you might have latest variations of Groovy and Java put in in your laptop.

Install Java and Groovy

Groovy relies on Java and requires a Java set up as nicely. A latest and/or respectable model of Java and Groovy may already be in your Linux distribution’s repositories, or you possibly can obtain and set up Groovy from the Apache Groovy website. possibility for Linux customers is SDKMan, which can be utilized to get a number of variations of Java, Groovy, and lots of different associated instruments. For this text, I’m utilizing SDK’s releases of:

  • Java: model 11.0.12-open of OpenJDK 11
  • Groovy: model 3.0.8.

Back to the issue: maps

First, in my expertise, maps and lists (or no less than arrays) usually find yourself in the identical program. For instance, processing an enter file is similar to passing over an inventory; usually, I try this once I wish to categorize information encountered within the enter file (or checklist), storing some form of worth in lookup tables, that are simply maps.

Second, Java 8 launched the entire Streams performance and lambdas (or nameless capabilities). In my expertise, changing enter information (or lists) into maps usually includes utilizing Java Streams. Moreover, Java Streams are at their most versatile when coping with streams of typed objects, offering grouping and accumulation amenities out of the field.

Employee checklist processing in Java

Here’s a concrete instance primarily based on these fictitious worker information. Below is a Java program that defines an Employee class to carry the worker data, builds an inventory of Employee cases, and processes that checklist in a couple of other ways:

     1  import java.lang.*;
     2  import java.util.Arrays;
       
     3  import java.util.Locale;
     4  import java.time.format.DateTimeFormatter;
     5  import java.time.LocalDate;
     6  import java.time.format.DateTimeParseException;
     7  import java.textual content.QuantityFormat;
     8  import java.textual content.ParseException;
       
     9  import java.util.stream.Collectors;
       
    10  public class Test31 {
       
    11      static public void fundamental(String args[]) {
       
    12          var employeeList = Arrays.asList(
    13              new Employee("Tiger Nixon", "System Architect",
    14                  "Edinburgh", "5421", "2011/04/25", "$320,800"),
    15              new Employee("Garrett Winters", "Accountant",
    16                  "Tokyo", "8422", "2011/07/25", "$170,750"),
                                                        ...
   
    81              new Employee("Martena Mccray", "Post-Sales support",
    82                  "Edinburgh", "8240", "2011/03/09", "$324,050"),
    83              new Employee("Unity Butler", "Marketing Designer",
    84                  "San Francisco", "5384", "2009/12/09", "$85,675")
    85          );
       
    86          // calculate the common wage throughout the whole firm
       
    87          var companyAvgSal = employeeList.
    88              stream().
    89              acquire(Collectors.averagingDouble(Employee::getSalary));
    90          System.out.println("company avg salary = " + companyAvgSal);
       
    91          // calculate the common wage for every location,
    92          //     evaluate to the corporate common
       
    93          var locationAvgSal = employeeList.
    94              stream().
    95              acquire(Collectors.groupingBy((Employee e) ->
    96                  e.getLocation(),
    97                      Collectors.averagingDouble(Employee::getSalary)));
    98          locationAvgSal.forEach((ok,v) ->
    99              System.out.println(ok + " avg salary = " + v +
   100                  "; diff from avg company salary = " +
   101                  (v - companyAvgSal)));
       
   102          // present the staff in Edinburgh method #1
       
   103          System.out.print("employee(s) in Edinburgh (approach #1):");
   104          var staffInEdinburgh = employeeList.
   105              stream().
   106              filter(e -> e.getLocation().equals("Edinburgh")).
   107              acquire(Collectors.toList());
   108          staffInEdinburgh.
   109              forEach(e ->
   110                  System.out.print(" " + e.getSurname() + "," +
   111                      e.getGivenName()));
   112          System.out.println();
       
       
   113          // group staff by location
       
   114          var employeesByLocation = employeeList.
   115              stream().
   116              acquire(Collectors.groupingBy(Employee::getLocation));
       
   117          // present the staff in Edinburgh method #2
       
   118          System.out.print("employee(s) in Edinburgh (approach #2):");
   119          employeesByLocation.get("Edinburgh").
   120              forEach(e ->
   121                  System.out.print(" " + e.getSurname() + "," +
   122                      e.getGivenName()));
   123          System.out.println();
       
   124      }
   125  }
       
   126  class Employee {
   127      personal String surname;
   128      personal String givenName;
   129      personal String function;
   130      personal String location;
   131      personal int extension;
   132      personal LocalDate employed;
   133      personal double wage;
       
   134      public Employee(String fullName, String function, String location,
   135          String extension, String employed, String wage) {
   136          var nn = fullName.cut up(" ");
   137          if (nn.size > 1) {
   138              this.surname = nn[1];
   139              this.givenName = nn[0];
   140          } else {
   141              this.surname = nn[0];
   142              this.givenName = "";
   143          }
   144          this.function = function;
   145          this.location = location;
   146          attempt {
   147              this.extension = Integer.parseInt(extension);
   148          } catch (NumberFormatException nfe) {
   149              this.extension = 0;
   150          }
   151          attempt {
   152              this.employed = LocalDate.parse(employed,
   153                  DateTimeFormatter.ofPattern("yyyy/MM/dd"));
   154          } catch (DateTimeParseException dtpe) {
   155              this.employed = LocalDate.EPOCH;
   156          }
   157          attempt {
   158              this.wage = NumberFormat.getCurrencyInstance(Locale.US).
   159                  parse(wage).doubleValue();
   160          } catch (ParseException pe) {
   161              this.wage = 0d;
   162          }
   163      }
       
   164      public String getSurname() { return this.surname; }
   165      public String getGivenName() { return this.givenName; }
   166      public String getLocation() { return this.location; }
   167      public int getExtension() { return this.extension; }
   168      public LocalDate getHired() { return this.employed; }
   169      public double getSalary() { return this.wage; }
   170  }

Programming and improvement

Wow, that is a variety of code for a easy demo program! I’ll undergo it in chunks first.

Starting on the finish, traces 126 by way of 170 outline the Employee class used to retailer worker information. The most vital factor to say right here is that the fields of the worker file are of various sorts, and in Java that usually results in defining this sort of class. You might make this code a bit extra compact through the use of Project Lombok’s @Data annotation to mechanically generate the getters (and setters) for the Employee class. In newer variations of Java, I can declare these types of issues as a file somewhat than a category, because the complete level is to retailer information. Storing the info as an inventory of Employee cases facilitates using Java streams.

Lines 12 by way of 85 create the checklist of Employee cases, so now you’ve got already handled 119 of 170 traces.

There are 9 traces of import statements up entrance. Interestingly, there aren’t any map-related imports! This is partly as a result of I’m utilizing stream strategies that yield maps as their outcomes, and partly as a result of I’m utilizing the var key phrase to declare variables, so the kind is inferred by the compiler.

The attention-grabbing components of the above code occur in traces 86 by way of 123.

In traces 87-90, I convert employeeList right into a stream (line 88) after which use acquire() to use the Collectors.averagingDouble() technique to the Employee::getSalary (line 89) technique to calculate the common wage throughout the entire firm. This is pure useful checklist processing; no maps are concerned.

In traces 93-101, I convert employeeList right into a stream once more. I then use the Collectors.groupingBy() technique to create a map whose keys are worker places, returned by e.getLocation(), and whose values are the common wage for every location, returned by Collectors.averagingDouble() once more utilized to the Employee::getSalary technique utilized to every worker within the location subset, somewhat than the whole firm. That is, the groupingBy() technique creates subsets by location, that are then averaged. Lines 98-101 use forEach() to step by way of the map entries printing location, common wage, and the distinction between the placement averages and firm common.

Now, suppose you needed to take a look at simply these staff situated in Edinburgh. One method to accomplish that is proven in traces 103-112, the place I exploit the stream filter() technique to create an inventory of solely these staff primarily based in Edinburgh and the forEach() technique to print their names. No maps right here, both.

Another method to remedy this downside is proven in traces 113-123. In this technique, I create a map the place every entry holds an inventory of staff by location. First, in traces 113-116, I exploit the groupingBy() technique to provide the map I need with keys of worker places whose values are sublists of staff at that location. Then, in traces 117-123, I exploit the forEach() technique to print out the sublist of names of staff on the Edinburgh location.

When we compile and run the above, the output is:

firm avg wage = 292082.5
San Francisco avg wage = 284703.125; diff from avg firm wage = -7379.375
New York avg wage = 410158.3333333333; diff from avg firm wage = 118075.83333333331
Singapore avg wage = 357650.0; diff from avg firm wage = 65567.5
Tokyo avg wage = 206087.5; diff from avg firm wage = -85995.0
London avg wage = 322476.25; diff from avg firm wage = 30393.75
Edinburgh avg wage = 261940.7142857143; diff from avg firm wage = -30141.78571428571
Sydney avg wage = 90500.0; diff from avg firm wage = -201582.5
worker(s) in Edinburgh (method #1): Nixon,Tiger Kelly,Cedric Frost,Sonya Flynn,Quinn Rios,Dai Joyce,Gavin Mccray,Martena
worker(s) in Edinburgh (method #2): Nixon,Tiger Kelly,Cedric Frost,Sonya Flynn,Quinn Rios,Dai Joyce,Gavin Mccray,Martena

Employee checklist processing in Groovy

Groovy has at all times offered enhanced amenities for processing lists and maps, partly by extending the Java Collections library and partly by offering closures, that are considerably like lambdas.

One end result of that is that maps in Groovy can simply be used with several types of values. As a outcome, you possibly can’t be pushed into making the auxiliary Employee class; as an alternative, you possibly can simply use a map. Let’s study a Groovy model of the identical performance:

     1  import java.util.Locale
     2  import java.time.format.DateTimeFormatter
     3  import java.time.LocalDate
     4  import java.time.format.DateTimeParseException
     5  import java.textual content.QuantityFormat
     6  import java.textual content.ParseException
       
     7  def employeeList = [
     8      ["Tiger Nixon", "System Architect", "Edinburgh",
     9          "5421", "2011/04/25", "$320,800"],
    10      ["Garrett Winters", "Accountant", "Tokyo",
    11          "8422", "2011/07/25", "$170,750"],

                           ...

    76      ["Martena Mccray", "Post-Sales support", "Edinburgh",
    77          "8240", "2011/03/09", "$324,050"],
    78      ["Unity Butler", "Marketing Designer", "San Francisco",
    79          "5384", "2009/12/09", "$85,675"]
    80  ].acquire { ef ->
    81      def surname, givenName, function, location, extension, employed, wage
    82      def nn = ef[0].cut up(" ")
    83      if (nn.size > 1) {
    84          surname = nn[1]
    85          givenName = nn[0]
    86      } else {
    87          surname = nn[0]
    88          givenName = ""
    89      }
    90      function = ef[1]
    91      location = ef[2]
    92      attempt {
    93          extension = Integer.parseInt(ef[3]);
    94      } catch (NumberFormatException nfe) {
    95          extension = 0;
    96      }
    97      attempt {
    98          employed = LocalDate.parse(ef[4],
    99              DateTimeFormatter.ofPattern("yyyy/MM/dd"));
   100      } catch (DateTimeParseException dtpe) {
   101          employed = LocalDate.EPOCH;
   102      }
   103      attempt {
   104          wage = NumberFormat.getCurrencyInstance(Locale.US).
   105              parse(ef[5]).doubleValue();
   106      } catch (ParseException pe) {
   107          wage = 0d;
   108      }
   109      [surname: surname, givenName: givenName, role: role,
   110          location: location, extension: extension, hired: hired, salary: salary]
   111  }
       
   112  // calculate the common wage throughout the whole firm
       
   113  def companyAvgSal = employeeList.common { e -> e.wage }
   114  println "company avg salary = " + companyAvgSal
       
   115  // calculate the common wage for every location,
   116  //     evaluate to the corporate common
       
   117  def locationAvgSal = employeeList.groupBy { e ->
   118      e.location
   119  }.collectEntries { l, el ->
   120      [l, el.average { e -> e.salary }]
   121  }
   122  locationAvgSal.every { l, a ->
   123      println l + " avg salary = " + a +
   124          "; diff from avg company salary = " + (a - companyAvgSal)
   125  }
       
   126  // present the staff in Edinburgh method #1
       
   127  print "employee(s) in Edinburgh (approach #1):"
   128  def staffInEdinburgh = employeeList.findAll { e ->
   129      e.location == "Edinburgh"
   130  }
   131  staffInEdinburgh.every { e ->
   132      print " " + e.surname + "," + e.givenName
   133  }
   134  println()
       
   135  // group staff by location
       
   136  def employeesByLocation = employeeList.groupBy { e ->
   137      e.location
   138  }
       
   139  // present the staff in Edinburgh method #2
       
   140  print "employee(s) in Edinburgh (approach #1):"
   141  employeesByLocation["Edinburgh"].every { e ->
   142      print " " + e.surname + "," + e.givenName
   143  }
   144  println()

Because I’m simply writing a script right here, I need not put this system physique inside a technique inside a category; Groovy handles that for us.

In traces 1-6, I nonetheless have to import the courses wanted for the info parsing. Groovy imports fairly a little bit of helpful stuff by default, together with java.lang.* and java.util.*.

In traces 7-90, I exploit Groovy’s syntactic assist for lists as comma-separated values bracketed by [ and ]. In this case, there’s a checklist of lists; every sublist is the worker information. Notice that you simply want the in entrance of the $ within the wage area. This is as a result of a $ occurring inside a string surrounded by double quotes signifies the presence of a area whose worth is to be interpolated into the string. An various could be to make use of single quotes.

But I do not wish to work with an inventory of lists; I might somewhat have an inventory of maps analogous to the checklist of Employee class cases within the Java model. I exploit the Groovy Collection .acquire() technique in traces 90-111 to take aside every sublist of worker information and convert it right into a map. The acquire technique takes a Groovy Closure argument, and the syntax for making a closure surrounds the code with { and } and lists the parameters as a, b, c -> in a way just like Java’s lambdas. Most of the code seems fairly just like the constructor technique within the Java Employee class, besides that there are gadgets within the sublist somewhat than arguments to the constructor. However, the final two traces—

[surname: surname, givenName: givenName, role: role,

    location: location, extension: extension, hired: hired, salary: salary]

—create a map with keys surname, givenName, function, location, extension, employed, and wage. And, since that is the final line of the closure, the worth returned to the caller is that this map. No want for a return assertion. No have to quote these key values; Groovy assumes they’re strings. In truth, in the event that they have been variables, you would want to place them in parentheses to point the necessity to consider them. The worth assigned to every key seems on its proper aspect. Note that this can be a map whose values are of various sorts: The first 4 are String, then int, LocalDate, and double. It would have been potential to outline the sublists with parts of these differing kinds, however I selected to take this method as a result of the info would usually be learn in as string values from a textual content file.

The attention-grabbing bits seem in traces 112-144. I’ve stored the identical form of processing steps as within the Java model.

In traces 112-114, I exploit the Groovy Collection common() technique, which like acquire() takes a Closure argument, right here iterating over the checklist of worker maps and choosing out the wage worth. Note that utilizing these strategies on the Collection class means you do not have to learn to remodel lists, maps, or another component to streams after which study the stream strategies to deal with your calculations, as in Java. For those that like Java Streams, they’re obtainable in newer Groovy variations.

In traces 115-125, I calculate the common wage by location. First, in traces 117-119, I remodel employeeList, which is an inventory of maps, right into a map, utilizing the Collection groupBy() technique, whose keys are the placement values and whose values are linked sublists of the worker maps pertaining to that location. Then I course of these map entries with the collectEntries() technique, utilizing the common() technique to compute the common wage for every location.

Note that collectEntries() passes every key (location) and worth (worker sublist at that location) into the closure (the l, el -> string) and expects a two-element checklist of key (location) and worth (common wage at that location) to be returned, changing these into map entries. Once I’ve the map of common salaries by location, locationAvgSal, I can print it out utilizing the Collection every() technique, which additionally takes a closure. When every() is utilized to a map, it passes in the important thing (location) and worth (common wage) in the identical approach as collectEntries().

In traces 126-134, I filter the employeeList to get a sublist of staffInEdinburgh, utilizing the findAll() technique, which is analogous to the Java Streams filter() technique. And once more, I exploit the every() technique to print out the sublist of staff in Edinburgh.

In traces 135-144, I take the choice method of grouping the employeeList right into a map of worker sublists at every location, employeesByLocation. Then in traces 139-144, I choose the worker sublist at Edinburgh, utilizing the expression employeesByLocation[“Edinburgh”] and the every() technique to print out the sublist of worker names at that location.

Why I usually favor Groovy

Maybe it is simply my familiarity with Groovy, constructed up over the past 12 years or so, however I really feel extra comfy with the Groovy method to enhancing Collection with all these strategies that take a closure as an argument, somewhat than the Java method of changing the checklist, map, or no matter is at hand to a stream after which utilizing streams, lambdas, and information courses to deal with the processing steps. I appear to spend so much extra time with the Java equivalents earlier than I get one thing working.

I’m additionally an enormous fan of sturdy static typing and parameterized sorts, resembling Map,worker> ,worker>as present in Java. However, on a day-to-day foundation, I discover that the extra relaxed method of lists and maps accommodating differing kinds does a greater job of supporting me in the actual world of knowledge with out requiring a variety of additional code. Dynamic typing can undoubtedly come again to chew the programmer. Still, even figuring out that I can flip static kind checking on in Groovy, I guess I have never performed so greater than a handful of occasions. Maybe my appreciation for Groovy comes from my work, which normally includes bashing a bunch of knowledge into form after which analyzing it; I’m definitely not your common developer. So is Groovy actually a extra Pythonic Java? Food for thought.

I might like to see in each Java and Groovy a couple of extra amenities like common()and averagingDouble(). Two-argument variations to provide weighted averages and statistical strategies past averaging—like median, normal deviation, and so forth—would even be useful. Tabnine affords attention-grabbing solutions on implementing a few of these.

Groovy assets

The Apache Groovy site has a variety of nice documentation. Other good sources embody the reference web page for Groovy enhancements to the Java Collection class, the extra tutorial-like introduction to working with collections, and Mr. Haki. The Baeldung site gives a variety of useful how-tos in Java and Groovy. And a very nice cause to study Groovy is to study Grails, a splendidly productive full-stack internet framework constructed on high of wonderful elements like Hibernate, Spring Boot, and Micronaut.

Most Popular

To Top