java8 make immutable List after removing null elements - arraylist

I know Arrays.asList creates immutable List. My requirement is to create immutable List<Student> with no null elements in it. So I am creating new ArrayList() first then removing null elements using java 8 filters as shown below:
List<Student> list = new ArrayList<>();
list.add(makeStudent("ram", 'M'));
list.add(makeStudent("sathya", 'F'));
list.add(makeStudent(null, 'M'));
list.add(makeStudent("sri", 'M'));
list = list.stream().filter(s -> s != null).collect(Collectors.toList());
Below is the makeStudent method, it returns null if name is not available:
private Student makeStudent(String name, char gender) {
return null != name ? new Student(name, gender) : null;
}
I thought Collectors.toList() will create immutable. But I was wrong. The list is still mutable here. Is there a better way to make the list clean (remove null) and immutable in single line using java 8?

You might rewrite your code to this one:
list = list.stream()
.filter(Objects::nonNull)
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
Collector collectingAndThen allow you to wrap result collection

Even though you specified Java 8, here is how you can filter an existing List into a fully immutable List that you're looking for with Java 9:
list = list.stream()
.filter(Objects::nonNull)
.collect(Collectors.toCollection(List::of));

using Collections.unmodifiableList to wrap the List collected by the stream.
list = Collections.unmodifiableList(list.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList()));
the Collectors.toList() method do the things like as reduce:
//pseudo-code
Collectors.reduce(ArrayList::new, List::add, List::addAll)
IF you passing an immutable List to reduce you can't collect any items from the stream into the List since it is immutable.

You can either use Collections.unmodifiableList as suggested in other answers or you can also convert stream to array and then use Arrays.asList.
list = Arrays.asList(list.stream()
.filter(Objects::nonNull)
.toArray(Student[]::new));

Related

Convert LinkedHasSet from one type to another

I have a very simple problem, I need to convert a LinkedHashSet that holds one type of object, into another.
So basically what I want to do is something like this(if map could return anything else than TypeB:
LinkedHashSet<TypeA> firstSet
LinkedHashSet<TypeB> secondSet = firstSet.map {
TypeB(firstSet.value1, firstSet.value2)
}
This is mostly written to signalize what I want to achieve, of course it doesn't work. Could someone help me write this in Kotlin?
map returns a List, but you can use mapTo to insert the resulting elements directly into a collection that you provide as its first argument. This collection is also returned so you can assign it to secondSet:
val secondSet: LinkedHashSet<TypeB> = firstSet.mapTo(LinkedHashSet<TypeB>()) {
TypeB(it.value1, it.value2)
}
This is more efficient than using map because it avoids creating an intermediate List to hold the results.

Proper way to operate collections in StateFlow

I'm creating MutableStateFlow like this:
val intSet = MutableStateFlow(HashSet<Int>())
And in some moment later I want to update collection in this flow:
intSet.value.add(0)
And this doesn't seem to work (the collection updates, but observers are not notified).
The way that I found it working:
val list = HashSet<Int>(intSet.value)
list.add(0)
intSet.value = list
But it creates copy of the collection, so it doesn't look proper for me. Is there any simpler way to update collection in StateFlow?
MutableFlow does not check for changes in the content of collections. Only when the collection reference has changed it will emit the change.
Use immutable Set and use the += operator to add new elements. This will basically, create new Set and will trigger the change.
val intSetFlow = MutableStateFlow(setOf<Int>())
intSetFlow.value += 0

What is the use of emptyArray() in Kotlin

I was learning emptyArray() in Kotlin, but I can't assign values in it (which is for obvious) and neither I can set it's size. What is the use of emptyArray in Kotlin?
There are cases where you want to fallback to an empty array or empty list. For example:
return someInputArray?.filterNotNull() ?: emptyArray()
In this case if I have a valid input array, I filter out null values, otherwise if the source array was null I return an empty array. Therefore an array is always returned instead of a null. Empty lists and arrays are probably more common than passing around nullable lists or arrays in Kotlin.
So yes, it is empty and you cannot add to it, just as no JVM array is expandable once allocated.
Similar to the java.util.Collections.emptyList() methods and its counterparts for Maps, etc., these methods are handy when you are calling a method which takes an array/collection as argument, but you don't want/need to provide any elements in that collection. You can either instantiate a new empty array/collection or use one of the helper methods.
If the above use case is very common in your scenario, you are safing memory by using the helpers as always the same instance is reused instead of creating new instances again and again. Otherwise it's mainly "syntax candy" making your code more readable and easier to understand.
emptyArray() function returns an empty array of size 0. it doesn't take any paramters (Nor size, neither elements). you can use it as rvalue in assignment operations. You can use empty array to dump all the values in a pre-existing arrays and set it's size to 0.
Note: it is better to return an empty than returning an array of nulls, as it'll not create any null point exceptions in further operations.
var array=arrayOf(1,2,3,4,5);
array = emptyArray() //The array datais dumped and size is set to 0;
array = arrayOf(0,1,2)
Arrays are fixed size containers. The emptyArray() function creates an array of 0 length, and you really can't do much with this. You can't store anything in it, and you can't resize it.
A common use case where you need an empty array would be as a default value for a property that will be set to a different array at a later point. This can be better than storing null in that property by default, because you don't have to deal with handling the possible null value when your code uses the array. For example, you can safely iterate over an empty array.
For a simple example:
class NumberHolder {
var numbers: Array<Int> = emptyArray()
fun printNumbers() {
numbers.forEach { println(it) }
}
}
The usage of this class would look something like this:
val nh = NumberHolder()
nh.printNumbers() // prints nothing
nh.numbers = arrayOf(1, 2, 3)
nh.printNumbers() // prints 1, 2, and 3 on separate lines

How to search by key in hashset?

I wanted a collection that would contain a bunch of unique strings. Hashset seemed to be the best option since it removes duplicates and is quick about it.
However how would i go about finding an item in the hash set? I could just loop through all the elements but wouldn't that defeat the purpose of being a hashset? Essentially i just want a dictionary where the key is the value.
Hashsets are not used for searching. They are used to treat collection as a whole. You can use it as for what mathematical sets are used for: to create unions, intersections, or for checks of duplication etc.
The dictionaries and hashsets are similar in internal implementation, since they are based on hashtables. But sets do not have keys, since they are not allowed for the lookup of the elements. You must use dictionary for it. If you want to ask a set for an element, you will have to enumerate thru them all.
In the same way, you should not use Queues, stacks or linked list for looking for an element.
To have only strings in a dictionary you can use it like:
IDictionary<string, string dictionary =
new Dictionary<string, string>();
dictionary.Add("Foo", "Foo");
dictionary.Add("Bar", "Bar");
string stringThatILookFor = null;
if (dictionary.TryGetValue("Bar", out stringThatILookFor))
{
// Use stringThatILookFor, which has a value Bar
}
You need to be using Contains.
Dim hsStrings As HashSet(Of String) = {"a", "b", "c"}
If hsStrings.Contains("a") Then 'do smth

Standard Place for an Empty String Array in the JDK

Hi is there a standard place for accessing empty array constants in the JDK > 1.5.
When I want to do a conversion from a String Collection (e.g. ArrayList)to a String Array I find myself using my own
which is defined in my own Constants class:
public static final String[] EMPTY_STRING_ARRAY = new String[0];
And then in my client code something like:
String[] retVal = myStringList.toArray(Constants.EMPTY_STRING_ARRAY);
return retVal;
I was wondering if this is the "idiomatic" way of doing it or if I'm missing something
I get the impression from the brief search I did that this kind of thing is prevalent in many people's code.
Any ideas, answers, comment (aside from that I shouldn't really use String Arrays) greatly appreciated,
Cheers
Simon
I would recommend the following code improvement :
String[] retval = myStringList.toArray(new String[myStringList.size()]);
This way, the toArray method uses the provided, appropriately-sized array as a container, instead of creating a new one behind the scenes. From the ArrayList javadoc :
If the list fits in the specified
array, it is returned therein.
Otherwise, a new array is allocated
with the runtime type of the specified
array and the size of this list.
There are no array definitions like that in the JDK anywhere. The only two standard ways of doing it in the JDK are that which you list or
String ret[] = new String[myStringList.size()];
myStringList.toArray(ret);
return ret;