Working with collections

You are browsing legacy Javonet 1.5 (Java<>.NET bridge for Windows) documentation. Use the left side menu or click here to switch to latest Javonet 2.0 documentation. Javonet 2.0 allows you to use any module from JVM, CLR, Netcore, Python, Ruby, Perl, NodeJS on Windows, Linux and MacOs from any application created in Java, Clojure, Groovy, Kotlin, C#, F#, J#, VB.NET, Python, Perl, Ruby, JavaScript, TypeScript, C++ and GoLang

Data structures are one of the essential aspects of every piece of software that has ever been written. Any application, from simple command line util to scalable enterprise systems, constantly process various information, that very often require specific grouping and access strategies. This aspect is addressed by arrays and more advanced collection types available both in Java and .NET environments. By using the Javonet framework, users gain ability to easily and effectively work with data structures originating from the other platform.

I'll be working with the following pseudo-service class, which main purpose is to produce and consume various collection types.

using System;
using System.Collections.Generic;

namespace TestNamespace
{
	class CollectionService
	{
		public IList<string> GetList()
		{
			return new List<string> { "strings", "in", "list", "from", ".NET" };
		}

		public void UseList(List<string> list)
		{
			Console.WriteLine("[.NET] List contents:");

			foreach (string element in list)
			{
				Console.Write("{0} ", element);
			}
		}

		public HashSet<string> GetSet()
		{
			return new HashSet<string> { "strings", "in", "set", "from", ".NET" };
		}

		public void UseSet(ISet<string> set)
		{
			Console.WriteLine("[.NET] Set contents:");

			foreach (string element in set)
			{
				Console.Write("{0} ", element);
			}
		}

		public IDictionary<string, string> GetDictionary()
		{
			return new Dictionary<string, string>()
		  {
			{"key1","value1"},
			{"key2","value2"},
			{"key3","value3"}
		  };
		}

		public void UseDictionary(IDictionary<string, string> dictionary)
		{
			Console.WriteLine("[.NET] Dictionary contents:");

			foreach (string key in dictionary.Keys)
			{
				Console.Write("'{0}' = '{1}';  ", key, dictionary[key]);
			}
		}
	}
}

Retrieving and handling collections

Collection-type objects retrieved by the Javonet framework, are accessible via the NObject/JObject class instances on the Java side. Therefore working with these objects is really simple, as I'll show in the next few examples.

The following code retrieves an instance of a Set class by invoking the parameterless "GetSet" method of the CollectionService object. We can access various attributes of our set with the NObject.get(String propertyName, Object… args) or invoke any of its available methods using NObject.invoke(String methodName, Object… args). It's worth mentioning that the result type of these methods can be parametrized, relieving us from manual type-checking-and-casting steps.

In the example below, we're using this approach to check the size of the received Set object, as well as use the collection enumerator object, to iterate over the contained strings. Unfortunately, as the NObject class does not implement Java Iterable interface, it is not possible to simply use the NObject in the for-each loop. Also, since set does not allow for index-based access to the contained elements, using enumerator can be considered primary method of collection traversal.

I code in:
// Todo: activate Javonet and add reference to .NET library

NObject collectionService = Javonet.New("TestNamespace.CollectionService");
NObject list = collectionService.invoke("GetSet");

// check size
System.out.format("[Java] Set size: %d%n", list.<Integer>get("Count"));

// iterate over contents
System.out.format("[Java] Set contents:%n");

NObject enumerator = list.invoke("GetEnumerator");

while (enumerator.<Boolean>invoke("MoveNext")) {
    String current = enumerator.get("Current");
    System.out.format("%s  ", current);
}
// expected output:
// [Java] Set size: 5
// [Java] Set contents:
// strings  in  set  from  .NET

In contrast to sets, lists and dictionaries do allow its users for selective access to the contained objects. For this purpose, we'll be using the NObject.getIndex(Object index) method, which is only applicable for NObjects representing .NET types allowing for such access. In case you would try to invoke this method on a non-indexed .NET object, Javonet framework will throw a JavonetException, stating that given .NET class does not provide indexed values.

The following two examples present usage of this approach against List and Dictionary<String, String> objects retrieved from .NET code.

I code in:
// Todo: activate Javonet and add reference to .NET library

NObject collectionService = Javonet.New("TestNamespace.CollectionService");

NObject list = collectionService.invoke("GetList");

// check size
System.out.format("[Java] List size: %d%n", list.<Integer>get("Count"));

// get arbitrary value assigned to given key
System.out.format("[Java] Second element: %s%n", list.<String>getIndex(1));

// iterate over contents
System.out.format("[Java] List contents:%n");

NObject enumerator = list.invoke("GetEnumerator");

while (enumerator.<Boolean>invoke("MoveNext")) {
    String current = enumerator.get("Current");
    System.out.format("%s  ", current);
}
// expected output:
// [Java] List size: 5
// [Java] Second element: in
// [Java] List contents:
// strings  in  list  from  .NET

Dictionary:

I code in:
// Todo: activate Javonet and add reference to .NET library

NObject collectionService = Javonet.New("TestNamespace.CollectionService");

NObject dictionary = collectionService.invoke("GetDictionary");

// check size
System.out.format("[Java] Dictionary size: %d%n", dictionary.<Integer>get("Count"));

// get arbitrary value assigned to given key
System.out.format("[Java] Value for '%s' key: %s%n", "key1", dictionary.getIndex("key1"));

// iterate over contents
System.out.format("[Java] Dictionary contents:%n");

NObject enumerator = dictionary.invoke("GetEnumerator");

while (enumerator.<Boolean>invoke("MoveNext")) {
    NObject current = enumerator.get("Current");
    System.out.format("'%s' = '%s'%n", current.get("Key"), current.get("Value"));
}
// expected output:
// [Java] Dictionary size: 3
// [Java] Value for 'key1' key: value1
// [Java] Dictionary contents:
// 'key1' = 'value1'
// 'key2' = 'value2'
// 'key3' = 'value3'

Passing collections

Sending collection-type objects from Java to .NET is no different from passing any other object. All we are required to do is grab the NObject instance, representing specific .NET collection object and use it in the method call. In the following example, we'll instantiate, populate and send a simple .NET list of strings as an argument to the "UseList" method of our .NET CollectionService object. This should result in printing all of the list's contents to standard output stream.

As I'll be using parametrized versions of .NET collection types, I am assuming you are familiar with the Javonet-way of instantiating such objects. In case you're not, I strongly encourage you to have a look at respective section of Quick Guide, available on the Javonet home page.

Similarly as when retrieving collections from .NET, the approach for sending NObject instances as arguments of method calls, can also be applied for setting .NET object's properties etc.

I code in:
// Todo: activate Javonet and add reference to .NET library

// get generic type with a single type-parameter
NType listType = Javonet.getType("List`1", "String");

// instantiate new object of that type
NObject list = listType.create();

// use object, to add dictionary entries
list.invoke("Add", "Strings");
list.invoke("Add", "in");
list.invoke("Add", "the");
list.invoke("Add", "list");
list.invoke("Add", "from");
list.invoke("Add", "Java");

NObject collectionService = Javonet.New("TestNamespace.CollectionService");

collectionService.invoke("UseList", list);
// expected output:
// [.NET] List contents:
// Strings in the list from Java

Here's another example in which we're creating a Dictionary<String,String> object, populate it and pass it as an argument to our .NET CollectionService object's "UseDictionary" method. Exactly as in previous example, we should get contents of our dictionary object in the standard output stream.

I code in:
// Todo: activate Javonet and add reference to .NET library

// get generic type with two type-parameters
NType dictionaryType = Javonet.getType("Dictionary`2", "String", "String");

// instantiate new object of that type
NObject dictionary = dictionaryType.create();

// use object, to add dictionary entries
dictionary.invoke("Add", "key1", "value1");
dictionary.invoke("Add", "key2", "value2");
dictionary.invoke("Add", "key3", "value3");

NObject collectionService = Javonet.New("TestNamespace.CollectionService");

collectionService.invoke("UseDictionary", dictionary);
// expected output:
// [.NET] Dictionary contents:
// 'key1' = 'value1';  'key2' = 'value2';  'key3' = 'value3';

Summary

In this article we've focused on working with collections using Javonet framework. We've briefly explained basic techniques of handling collection objects, covering retrieval, enumeration- and index-based access to their contents, as well as instantiation and usage as arguments in the method calls.