In the Kotlin collection functions, we have
setOf, mutableSetOf, sortedSetOf, linkedSetOf and hashSetOf.
The first 3 is understood their differences. But what's the last two?
How are they different from others and from themselves?
You know that linkedSetOf returns a LinkedHashSet whereas hashSetOf returns a HashSet. The difference between LinkedHashSet and HashSet is that LinkedHashSet maintains the order of elements with which they were either inserted into or removed from the set whereas HashSet doesn't maintain the order.
Observe the example below (Kotlin playground, if you want to see it in action)
val set: LinkedHashSet<Int> = linkedSetOf(1, 3, 2)
println(set) // [1, 3, 2]
set.remove(3)
set += listOf(5, 4)
println(set) // [1, 2, 5, 4]
val hashSet: HashSet<Int> = hashSetOf(1, 3, 2)
println(hashSet) // [1, 2, 3]
hashSet.remove(3)
hashSet += listOf(5, 4)
println(hashSet) // [1, 2, 4, 5]
If you look at how the set is printed in different instances of addition and removal of elements, you would see that in the case of LinkedHashSet, the order in which the elements were added or removed is maintained and printed in the same order when iterated over.
Note: Just to show that HashSet doesn't sort the elements, below is an example (Kotlin playground)
val linkedHashSet = linkedSetOf("aaa","bbb","ccc")
println(linkedHashSet) // [aaa, bbb, ccc]
linkedHashSet.remove("ccc")
linkedHashSet += listOf("ddd","zzz")
println(linkedHashSet) // [aaa, bbb, ddd, zzz]
val hashSet = hashSetOf("aaa","bbb","ccc")
println(hashSet) // [aaa, ccc, bbb]
hashSet.remove("ccc")
hashSet += listOf("ddd","zzz")
println(hashSet) // [aaa, bbb, zzz, ddd]
linkedSetOf returns a LinkedHasSet
hasSetOf returns a HashSet
The main difference between HashSet and LinkedHasSet is that LinkedHasSet maintains insertion order (order in which elements are inserted into LinkedHashSet). On the other hand HashSet doesn't maintain any order.
Related
Pretty much the title. I want to map through a list to create a new list, but the logic for transforming each element depends on previous values that have been already transformed.
For a simple example, I have a list val myList = [1, 2, 3, 4, 5] and I want to map through each value, where each new value is the sum of the current element plus the previous transformed element. Meaning, I want the result to be [1, 3, 6, 10, 15]. This is not a real scenario, just for sake of example.
I can map through my list but I don't know how to reference the new list that's currently being built:
myList.map { it + list_that's_currently_being_built[i-1] }
runningReduce
fun main() {
val myList = listOf(1, 2, 3, 4, 5)
val result = myList.runningReduce { acc, value -> acc + value }
println(result) // [1, 3, 6, 10, 15]
}
As stated in the answer https://stackoverflow.com/a/57115894/3286489, the asReversed() will generate a reversed list, that value will be changed if the original list has changed its element within.
val list = mutableListOf(0, 1, 2, 3, 4, 5)
val asReversed = list.asReversed()
val reversed = list.reversed()
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
list[0] = 10
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
Outputs
Original list: [0, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
Original list: [10, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 10]
reversed: [5, 4, 3, 2, 1, 0]
To me, that means only if the original list is a MutableList then it can change it's value within.
However, if the original List is an immutable List, it's value cannot be changed, this essentially make asReversed() and reversed() has not distinct difference on it, right?
i.e.
val list = listOf(0, 1, 2, 3, 4, 5)
val asReversed = list.asReversed() // This is the same as below?
val reversed = list.reversed() // This is the same as above?
Did I miss any scenario they are still different?
Updated
I even change what it contains as mutableList
val list = listOf(
mutableListOf(1),
mutableListOf(2),
mutableListOf(3),
mutableListOf(4),
mutableListOf(5),
mutableListOf(6))
val asReversed = list.asReversed()
val reversed = list.reversed()
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
list[0][0] = 10
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
Output
Original list: [[1], [2], [3], [4], [5], [6]]
asReversed: [[6], [5], [4], [3], [2], [1]]
reversed: [[6], [5], [4], [3], [2], [1]]
Original list: [[5], [2], [3], [4], [5], [6]]
asReversed: [[6], [5], [4], [3], [2], [10]]
reversed: [[6], [5], [4], [3], [2], [10]]
This will change for both asReversed and reversed result
The most important difference between the two is, that asReversed is always just a view on the original list. That means, if you alter the original list, the result of asReversed will always contain all the updated information. In the end it's just a view.
reversed however always gives you a new disconnected list of the original list. Note however that when you deal with object references within the original list (like lists in a list or other kind of objects that are referenceable) then you will see all the adaptations of the original object of that list also adapted in the reversed ones, regardless of whether you used reversed or asReversed.
Regarding your update it is unfortunate you used such a list that contains references. With your original list however the difference becomes much clearer:
val asReversed = list.asReversed()
val reversed = list.reversed()
fun printAll(msg : String) {
println(msg)
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
}
printAll("Initial ----")
list as MutableList
list[2] = 1000
printAll("mutating original list ----")
reversed as MutableList
reversed[4] = 3000
printAll("mutating reversed list ----")
As asReversed returns you a ReversedListReadOnly-type, you can't easily cast it to a MutableList, but if it would be possible, the changes in asReversed would be reflected in the original list, but not in the reversed. The output of the above is the following:
Initial ----
Original list: [0, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
mutating original list ----
Original list: [0, 1, 1000, 3, 4, 5]
asReversed: [5, 4, 3, 1000, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
mutating reversed list ----
Original list: [0, 1, 1000, 3, 4, 5]
asReversed: [5, 4, 3, 1000, 1, 0]
reversed: [5, 4, 3, 2, 3000, 0]
As you can see: the changes are nicely reflected in the original and the asReversed-list, but changes in the reversed are not reflected and also the reversed one will not contain the original list adaptations.
So, yes, you probably missed that scenario... the lists (reversed and asReversed) aren't equal, not even, if you have a readonly view as input, as noone can guarantee you that the list isn't altered... neither the original nor the asReversed one.
To add to other answers, this is an example of a general method-naming convention in Kotlin (and, to an extent in Java):
toNoun() methods convert an object into a new form that's independent of the original.
asNoun() methods return a view onto the existing object; changes in that will be reflected in the view and (if appropriate) vice versa.
verb() methods mutate the object directly (and don't return anything).
verbed() methods return a mutated copy of the object, leaving the original unchanged.
This question provides examples of two of those cases.
These conventions are very lightweight, read well, and are used fairly consistently. After you've seen a few, you know almost instinctively how a method will behave!
(It's not universal, though; for example, map() is a verb, but doesn't mutate its receiver. However, methods like map() and filter() long pre-date Kotlin, and so it's arguably better to stick to well-known existing names like that.)
Their difference actually amazed me!
Okay so by browsing the std-lib this is what I've found.
The reversed function actually creates a copy of whatever the Iterable is as a MutableList and reverses the list "really" and then returns it.
public fun <T> Iterable<T>.reversed(): List<T> {
if (this is Collection && size <= 1) return toList()
val list = toMutableList()
list.reverse()
return list
}
While when you call the asReversed(), it doesn't create a new list "by copying the elements".
It just creates an implementation of an Abstract list with delegation to the real list, and overriding the getter.
public fun <T> List<T>.asReversed(): List<T> = ReversedListReadOnly(this)
private open class ReversedListReadOnly<out T>(private val delegate: List<T>) : AbstractList<T>() {
override val size: Int get() = delegate.size
override fun get(index: Int): T = delegate[reverseElementIndex(index)]
}
So there's no overhead, since the list is immutable there is no need to touch any other part, neither need to copy the list i.e. no need for creating a new one and allocate its memory. It is simplified and just uses the original list.
Found this example here https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/as-reversed.html
val original = mutableListOf('a', 'b', 'c', 'd', 'e')
val originalReadOnly = original as List<Char>
val reversedA = originalReadOnly.asReversed()
val reversedB = originalReadOnly.reversed()
println(original) // [a, b, c, d, e]
println(reversedA) // [e, d, c, b, a]
println(reversedB)
// changing the original list affects its reversed view
original.add('f')
println(original) // [a, b, c, d, e, f]
println(reversedA) // [f, e, d, c, b, a]
println(reversedB) // [e, d, c, b, a]
original[original.lastIndex] = 'z'
println(original) // [a, b, c, d, e, z]
println(reversedA) // [z, e, d, c, b, a]
println(reversedB) // [e, d, c, b, a]
It does show that a List can still be changed if it was originally from a MutableList, where we change the MutableList. Hence that makes asReversed() is still different from reversed() in this case, since there's a way to change the List by changing the original MutableList
I noticed that Kotlin has two built in methods reversed() and asReversed()
Is there any difference between these two? or are they essentially just doing the exact same thing?
In Kotlin, both reversed and asReversed have their own unique functions.
The Reverse function returns a list with elements in reversed: order.
Reversed Function
Whereas, the asReversed function returns a reversed read-only view of the original List i.e., all changes made in the original list will be reflected in the reversed one.
asReversed Function
The difference between the two are that once the asReversed() function has been used, any changes in the original list will be reflected in the reversed list as well.
But the same doesn't hold valid or true when the reversed() function is being used. It's merely used to reverse a list.
Example:
val list = mutableListOf(0, 1, 2, 3, 4, 5)
val asReversed = list.asReversed()
val reversed = list.reversed()
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
list[0] = 10
println("Original list: $list")
println("asReversed: $asReversed")
println("reversed: $reversed")
Outputs
Original list: [0, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 0]
reversed: [5, 4, 3, 2, 1, 0]
Original list: [10, 1, 2, 3, 4, 5]
asReversed: [5, 4, 3, 2, 1, 10]
reversed: [5, 4, 3, 2, 1, 0]
Try it online!
As per the document
1. reversed()
It only returns a list (List<>) with elements in reversed order.
There are multiple definitions of this extension on different Objects like Array, List, etc.
Example of extension on
Array<>
/**
* Returns a list with elements in reversed order.
*/
public fun <T> Array<out T>.reversed(): List<T> {
if (isEmpty()) return emptyList()
val list = toMutableList()
list.reverse()
return list
}
List<>
/**
* Returns a list with elements in reversed order.
*/
public fun <T> Iterable<T>.reversed(): List<T> {
if (this is Collection && size <= 1) return toList()
val list = toMutableList()
list.reverse()
return list
}
2. asReversed()
It is only applicable to List<> and returns a reversed read-only view of the original List. All changes made in the original list will be reflected in the reversed one.
I want to split a Vec into some parts of equal length, and then map over them. I have an iterator resulting from a call to Vec's chunks() method. This may leave me with a part that will be smaller than other parts, which will be the last element generated by it.
To be sure that all parts have equal length, I just want to drop that last element and then call map() on what's left.
As Sebastian Redl points out, checking the length of each chunk is the better solution for your specific case.
To answer the question you asked ("Use all but the last element from an iterator"), you can use Iterator::peekable to look ahead one. That will tell you if you are on the last item or not and you can decide to skip processing it if so.
let things = [0, 1, 2, 3, 4];
let mut chunks = things.chunks(2).peekable();
while let Some(chunk) = chunks.next() {
if chunks.peek().is_some() {
print!("Not the last: ");
} else {
print!("This is the last: ")
}
println!("{:?}", chunk);
}
To be sure that all parts have equal length, I just want to drop that last element
Always dropping the last element won't do this. For example, if you evenly chunk up your input, then always dropping the last element would lose a full chunk. You'd have to do some pre-calculation to decide if you need to drop it or not.
You can filter() the chunks iterator on the slice's len() being the amount you passed to chunks():
let things = [0, 1, 2, 3, 4];
for chunk in things.chunks(2).filter(|c| c.len() == 2) {
println!("{:?}", chunk);
}
This prints
[0, 1]
[2, 3]
As of Rust 1.31, you can use the chunks_exact method as well:
let things = [0, 1, 2, 3, 4];
for chunk in things.chunks_exact(2) {
println!("{:?}", chunk);
}
This prints
[0, 1]
[2, 3]
Note that the returned iterator also has the method remainder if you need to get the uneven amount of items at the very end.
If the collection being iterated over implements std::iter::DoubleEndedIterator, you can use the next_back() method for a solution that is (probably) marginally more performant than Shepmaster's solution and a little cleaner:
let things = [0, 1, 2, 3, 4];
let mut chunks = things.chunks(2);
let last = chunks.next_back().unwrap();
println!("Last: {:?}", last);
for chunk in chunks {
println!("Not last: {:?}", chunk);
}
next_back() eats the last element of the iterator, so after calling next_back() the iterator can be used to iterate over everything else. The output of the segment above:
Last: [4]
Not last: [0, 1]
Not last: [2, 3]
Since Rust 1.31.0, you can use chunks_exact() to leave out the last chunk if it is not full.
For this particular situation, chunks_exact is what you want to use right now.
For the more general case of "taking all but the last n items of an iterator", Itertools has a method just for this: dropping_back.
Note that unlike methods of std::iter::Iterator, dropping_back is evaluated eagerly. Take care if your iterator chain is computation-heavy.
Is there a simple way to divide list into parts (maybe some lambda) in Kotlin?
For example:
[1, 2, 3, 4, 5, 6] => [[1, 2], [3, 4], [5, 6]]
Since Kotlin 1.2 you can use Iterable<T>.chunked(size: Int): List<List<T>> function from stdlib (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/chunked.html).
Given the list: val list = listOf(1, 2, 3, 4, 5, 6) you can use groupBy:
list.groupBy { (it + 1) / 2 }.map { it.value }
Or if your values are not numbers you can first assign an index to them:
list.withIndex()
.groupBy { it.index / 2 }
.map { it.value.map { it.value } }
Or if you'd like to save some allocations you can go a bit more manual way with foldIndexed:
list.foldIndexed(ArrayList<ArrayList<Int>>(list.size / 2)) { index, acc, item ->
if (index % 2 == 0) {
acc.add(ArrayList(2))
}
acc.last().add(item)
acc
}
The better answer is actually the one authored by VasyaFromRussia.
If you use groupBy, you will have to add and index and then post-process extracting the value from an IndexedValue object.
If you use chunked, you simply need to write:
val list = listOf(10, 2, 3, 4, 5, 6)
val chunked = list.chunked(2)
println(chunked)
This prints out:
[[10, 2], [3, 4], [5, 6]]
Nice way of dividing list is by the use of function partition. Unlike groupBy it doesn't divide list by keys but rather by predicate which gives out Pair<List, List> as a result.
Here's an example:
val (favorited, rest) = posts.partition { post ->
post.isFavorited()
}
favoritedList.addAll(favorited)
postsList.addAll(rest)
The API says there is a GroupBy function, which should do what you want.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/group-by.html
Or use sublist and break it up yourself
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/sub-list.html
If you want to divide a list into N parts.
(and not divide a list into parts of size N)
You can still use the chunked answer:
https://stackoverflow.com/a/48400664/413127
Only, first you need to find your chunk size.
val parts = 2
val list = listOf(10, 2, 3, 4, 5, 6)
val remainder = list.size % 2 // 1 or 0
val chunkSize = (list.size / parts) + remainder
val chunked = list.chunked(chunkSize)
println(chunked)
This prints out
[[10, 2, 3], [4, 5, 6]]
or when
val parts = 3
This prints out
[[10, 2], [3, 4], [5, 6]]
Interesting answer in Python here: Splitting a list into N parts of approximately equal length