I have the following data class
data class PuzzleBoard(val board: IntArray) {
val dimension by lazy { Math.sqrt(board.size.toDouble()).toInt() }
}
I read that data classes in Kotlin get equals()/hashcode() method for free.
I instantiated two objects.
val board1 = PuzzleBoard(intArrayOf(1,2,3,4,5,6,7,8,0))
val board2 = PuzzleBoard(intArrayOf(1,2,3,4,5,6,7,8,0))
But still, the following statements return false.
board1 == board2
board1.equals(board2)
In Kotlin data classes equality check, arrays, just like other classes, are compared using equals(...), which compares the arrays references, not the content. This behavior is described here:
So, whenever you say
arr1 == arr2
DataClass(arr1) == DataClass(arr2)
...
you get the arrays compared through equals(), i.e. referentially.
Given that,
val arr1 = intArrayOf(1, 2, 3)
val arr2 = intArrayOf(1, 2, 3)
println(arr1 == arr2) // false is expected here
println(PuzzleBoard(arr1) == PuzzleBoard(arr2)) // false too
To override this and have the arrays compared structurally, you can implement equals(...)+hashCode() in your data class using Arrays.equals(...) and Arrays.hashCode(...):
override fun equals(other: Any?): Boolean{
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as PuzzleBoard
if (!Arrays.equals(board, other.board)) return false
return true
}
override fun hashCode(): Int{
return Arrays.hashCode(board)
}
This code is what IntelliJ IDEA can automatically generate for non-data classes.
Another solution is to use List<Int> instead of IntArray. Lists are compared structurally, so that you won't need to override anything.
Kotlin implementation:
override fun equals(other: Any?): Boolean {
when (other) {
is User -> {
return this.userId == other.userId &&
this.userName == other.userName
}
else -> return false
}
}
For Data classes in Kotlin, hashcode() method will generate and return the same integer if parameters values are same for both objects.
val user = User("Alex", 1)
val secondUser = User("Alex", 1)
val thirdUser = User("Max", 2)
println(user.hashCode().equals(secondUser.hashCode()))
println(user.hashCode().equals(thirdUser.hashCode()))
Running this code will return True and False as when we created secondUser object we have passed same argument as object user, so hashCode() integer generated for both of them will be same.
also if you will check this:
println(user.equals(thirdUser))
It will return false.
As per hashCode() method docs
open fun hashCode(): Int (source)
Returns a hash code value for the object. The general contract of
hashCode is:
Whenever it is invoked on the same object more than once, the hashCode
method must consistently return the same integer, provided no
information used in equals comparisons on the object is modified.
If two objects are equal according to the equals() method, then calling
the hashCode method on each of the two objects must produce the same
integer result.
For more details see this discussion here
In Kotlin, equals() behaves differently between List and Array, as you can see from code below:
val list1 = listOf(1, 2, 3)
val list2 = listOf(1, 2, 3)
val array1 = arrayOf(1, 2, 3)
val array2 = arrayOf(1, 2, 3)
//Side note: using a==b is the same as a.equals(b)
val areListsEqual = list1 == list2// true
val areArraysEqual = array1 == array2// false
List.equals() checks whether the two lists have the same size and contain the same elements in the same order.
Array.equals() simply does an instance reference check. Since we created two arrays, they point to different objects in memory, thus not considered equal.
Since Kotlin 1.1, to achieve the same behavior as with List, you can use Array.contentEquals().
Source: Array.contentEquals() docs ;
List.equals() docs
The board field in the PuzzleBoard class is an IntArray, when compiled it is turned into a primitive integer array. Individual array elements are never compared when checking the equality of primitive integer arrays. So calling equals on int array returns false as they are pointing to different objects. Eventually, this results in getting false in the equals() method, even though array elements are the same.
Byte code check
Looking at the decompiled java byte code, the Kotlin compiler generates some functions of data classes for us.
This includes,
copy() function
toString() function - takes form ClassName(var1=val1, var2=val2, ...)
hashCode() function
equals() function
Hash code is generated by adding the hash code of individual variables and multiplying by 31. The reason for multiplying is that it can be replaced with the bitwise operator and according to experimental results, 31 and other numbers like 33, 37, 39, 41, etc. gave fever clashes when multiplied.
Take a look at decompiled java byte code of the Kotlin class PuzzleBoard which reveals the secrets of data classes.
#Metadata(
mv = {1, 7, 1},
k = 1,
d1 = {"\u0000(\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u0015\n\u0002\b\u0004\n\u0002\u0010\b\n\u0002\b\u0007\n\u0002\u0010\u000b\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\b\u0086\b\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\t\u0010\r\u001a\u00020\u0003HÆ\u0003J\u0013\u0010\u000e\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u0003HÆ\u0001J\u0013\u0010\u000f\u001a\u00020\u00102\b\u0010\u0011\u001a\u0004\u0018\u00010\u0001HÖ\u0003J\t\u0010\u0012\u001a\u00020\bHÖ\u0001J\t\u0010\u0013\u001a\u00020\u0014HÖ\u0001R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006R\u001b\u0010\u0007\u001a\u00020\b8FX\u0086\u0084\u0002¢\u0006\f\n\u0004\b\u000b\u0010\f\u001a\u0004\b\t\u0010\n¨\u0006\u0015"},
d2 = {"Lcom/aureusapps/androidpagingbasics/data/PuzzleBoard;", "", "board", "", "([I)V", "getBoard", "()[I", "dimension", "", "getDimension", "()I", "dimension$delegate", "Lkotlin/Lazy;", "component1", "copy", "equals", "", "other", "hashCode", "toString", "", "androidpagingbasics_debug"}
)
public final class PuzzleBoard {
#NotNull
private final Lazy dimension$delegate;
#NotNull
private final int[] board;
public final int getDimension() {
Lazy var1 = this.dimension$delegate;
Object var3 = null;
return ((Number)var1.getValue()).intValue();
}
#NotNull
public final int[] getBoard() {
return this.board;
}
public PuzzleBoard(#NotNull int[] board) {
Intrinsics.checkNotNullParameter(board, "board");
super();
this.board = board;
this.dimension$delegate = LazyKt.lazy((Function0)(new Function0() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke() {
return this.invoke();
}
public final int invoke() {
return (int)Math.sqrt((double)PuzzleBoard.this.getBoard().length);
}
}));
}
#NotNull
public final int[] component1() {
return this.board;
}
#NotNull
public final PuzzleBoard copy(#NotNull int[] board) {
Intrinsics.checkNotNullParameter(board, "board");
return new PuzzleBoard(board);
}
// $FF: synthetic method
public static PuzzleBoard copy$default(PuzzleBoard var0, int[] var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var1 = var0.board;
}
return var0.copy(var1);
}
#NotNull
public String toString() {
return "PuzzleBoard(board=" + Arrays.toString(this.board) + ")";
}
public int hashCode() {
int[] var10000 = this.board;
return var10000 != null ? Arrays.hashCode(var10000) : 0;
}
public boolean equals(#Nullable Object var1) {
if (this != var1) {
if (var1 instanceof PuzzleBoard) {
PuzzleBoard var2 = (PuzzleBoard)var1;
if (Intrinsics.areEqual(this.board, var2.board)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
Related
I am using an Array of non-optional values, and I want them to stay non-optional, but I can't use Array's default constructor because of problems described here.
Furthermore, the .also{} trick described in the linked won't work for me, because mine is not an array of some primitive type with its own special WhateverArray class.
Is there some Kotlin trick by which I can initialize my a below? Or must I resort to building some list and then converting it?
// please assume Stream<MyNonprimitiveType> magically gives me
// size() -> Int and
// next() -> MyNonprimitiveType
val stream : Stream<MyNonprimitiveType> = Stream<MyNonprimitiveType>()
val size : Int = stream.size()
val a : Array<MyNonprimitiveType> = ??? // use stream.next()
Here's a complete example doing what you want, without using a temporary list:
class Stream<T>(private val list: List<T>) {
val size = list.size;
private val it = list.iterator()
fun next(): T {
return it.next()
}
}
inline fun <reified T: Any> Stream<T>.toArray(): Array<T> {
val tmp: Array<T?> = arrayOfNulls(size)
for (i in 0 until size) {
tmp[i] = next()
}
return tmp as Array<T>
}
fun main() {
val stream : Stream<String> = Stream(listOf("a", "b"))
val a: Array<String> = stream.toArray()
println(Arrays.toString(a))
}
In the constructor of an Array is there a guarantee that the init function will be called for the indexes in an increasing order?
It would make sense but I did not find any such information in the docs:
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/-init-.html#kotlin.Array%24%28kotlin.Int%2C+kotlin.Function1%28%28kotlin.Int%2C+kotlin.Array.T%29%29%29%2Finit
There is no guarantee for this in the API.
TLDR: If you need the sequential execution, because you have some state that changes see bottom.
First lets have a look at the implementations of the initializer:
Native: It is implemented in increasing order for Kotlin Native.
#InlineConstructor
public constructor(size: Int, init: (Int) -> Char): this(size) {
for (i in 0..size - 1) {
this[i] = init(i)
}
}
JVM: Decompiling the Kotlin byte code for
class test {
val intArray = IntArray(100) { it * 2 }
}
to Java in Android Studio yields:
public final class test {
#NotNull
private final int[] intArray;
#NotNull
public final int[] getIntArray() {
return this.intArray;
}
public test() {
int size$iv = 100;
int[] result$iv = new int[size$iv];
int i$iv = 0;
for(int var4 = result$iv.length; i$iv < var4; ++i$iv) {
int var6 = false;
int var11 = i$iv * 2;
result$iv[i$iv] = var11;
}
this.intArray = result$iv;
}
}
which supports the claim that it is initialized in ascending order.
Conclusion: It commonly is implemented to be executed in ascending order.
BUT: You can not rely on the execution order, as the implementation is not guaranteed by the API. It can change and it can be different for different platforms (although both is unlikely).
Solution: You can initialize the array manually in a loop, then you have control about the execution order.
The following example outlines a possible implementation that has a stable initialisation with random values, e.g. for tests.
val intArray = IntArray(100).also {
val random = Random(0)
for (index in it.indices) {
it[index] = index * random.nextInt()
}
}
Starting from the version 1.3.50 Kotlin has guaranteed sequential array initialization order in its API documentation: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/-init-.html
The function init is called for each array element sequentially starting from the first one. It should return the value for an array element given its index.
I'm new to Kotlin (I have a Java background) and I can't seem to figure out how to check whether a string contains a match from a list of keywords.
What I want to do is check if a string contains a match from an array of keywords (case-insensitive please). If so, print out the keyword(s) that was matched and the string that contained the keyword. (I will be looping over a bunch of strings in a file).
Here's an MVE for starters:
val keywords = arrayOf("foo", "bar", "spam")
fun search(content: String) {
var match = <return an array of the keywords that content contained>
if(match.size > 0) {
println("Found match(es): " + match + "\n" + content)
}
}
fun main(args: Array<String>) {
var str = "I found food in the barn"
search(str) //should print out that foo and bar were a match
}
As a start (this ignores the 'match' variable and getting-a-list-of-keywords-matched), I tried using the following if statement according with what I found at this question,
if(Arrays.stream(keywords).parallel().anyMatch(content::contains))
but it put a squiggly line under "content" and gave me this error
None of the following functions can be called with the arguments
supplied: public operator fun CharSequence.contains(char: Char,
ignoreCase: Boolean = ...): Boolean defined in kotlin.text public
operator fun CharSequence.contains(other: CharSequence, ignoreCase:
Boolean = ...): Boolean defined in kotlin.text #InlineOnly public
inline operator fun CharSequence.contains(regex: Regex): Boolean
defined in kotlin.text
You can use the filter function to leave only those keywords contained in content:
val match = keywords.filter { it in content }
Here match is a List<String>. If you want to get an array in the result, you can add .toTypedArray() call.
in operator in the expression it in content is the same as content.contains(it).
If you want to have case insensitive match, you need to specify ignoreCase parameter when calling contains:
val match = keywords.filter { content.contains(it, ignoreCase = true) }
Another obvious choice is using a regex doing case-insensitive matching:
arrayOf("foo", "bar", "spam").joinToString(prefix = "(?i)", separator = "|").toRegex())
Glues together a pattern with a prefixed inline (?i) incase-sensitive modifier, and alternations between the keywords: (?i)foo|bar|spam
Sample Code:
private val keywords = arrayOf("foo", "bar", "spam")
private val pattern = keywords.joinToString(prefix = "(?i)", separator = "|")
private val rx = pattern.toRegex()
fun findKeyword(content: String): ArrayList<String> {
var result = ArrayList<String>()
rx.findAll(content).forEach { result.add(it.value) }
return result
}
fun main(args: Array<String>) {
println(findKeyword("Some spam and a lot of bar"));
}
The regex approach could be handy if you are after some more complex matching, e.g. non-/overlapping matches adding word boundaries \b, etc.
Here is my approach without Streams:
fun String.containsAnyOfIgnoreCase(keywords: List<String>): Boolean {
for (keyword in keywords) {
if (this.contains(keyword, true)) return true
}
return false
}
Usage:
"test string".containsAnyOfIgnoreCase(listOf("abc","test"))
I think Any is the efficient way.
fun findMatch(s: String, strings: List<String>): Boolean {
return strings.any { s.contains(it) }
}
fun main() {
val today = "Wednesday"
val weekend = listOf("Sat", "Sun")
println(if (findMatch(today, weekend)) "Yes" else "No") // No
}
reference: click here
I've just read a post by Basem Emara about creating a threadsafe array Type in Swift. While I glanced through the code example, I asked myself if there isn't a way to achieve this with quite less code.
Suppose I create this class:
// MARK: Class Declaration
class ThreadsafeArray<Element> {
// Private Variables
private var __array: [Element] = []
private var __arrayQueue: DispatchQueue = DispatchQueue(
label: "ThreadsafeArray.__concurrentArrayQueue",
attributes: .concurrent
)
}
// MARK: Interface
extension ThreadSafeArray {
// ReadWrite Variables
var threadsafe: [Element] {
get {
return self.__arrayQueue.sync {
return self.__array
}
}
set(newArray) {
self.__arrayQueue.async(flags: .barrier) {
self.__array = newArray
}
}
}
}
If, from now on, I only accessed the actual array through .threadsafe, would this suffice to make the array threadsafe?
Also, could I implement it a struct instead of a class to get the mutating checks as well?
I am aware that the objects inside this array would not be threadsafe themselves through this but this is not the point, so let's assume I only put threadsafe stuff in there.
(Of course, to avoid the calls to .threadsafe, I would make the shiny new class conform to ExpressibleByArrayLiteral, Collection and RangeReplaceableCollection, so I can use it like a normal array.
Edit
Meanwhile, I've tried testing it in a playground and have come to believe that it doesn't suffice.
Playground code:
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// Testing //
// Thread-unsafe array
func unsafeArray() {
var array: [Int] = []
var iterations: Int = 1000
let start: TimeInterval = Date().timeIntervalSince1970
DispatchQueue.concurrentPerform(iterations: iterations) { index in
let last: Int = array.last ?? 0
array.append(last + 1)
DispatchQueue.global().sync {
iterations -= 1
// Final loop
guard iterations <= 0 else { return }
print(String(
format: "Unsafe loop took %.3f seconds, count: %d.",
Date().timeIntervalSince1970 - start, array.count
))
}
}
}
// Thread-safe array
func safeArray() {
let array: ThreadsafeArray<Int> = ThreadsafeArray<Int>()
var iterations: Int = 1000
let start: TimeInterval = Date().timeIntervalSince1970
DispatchQueue.concurrentPerform(iterations: iterations) { index in
let last: Int = array.threadsafe.last ?? 0
array.threadsafe.append(last + 1)
DispatchQueue.global().sync {
iterations -= 1
// Final loop
guard iterations <= 0 else { return }
print(String(
format: "Safe loop took %.3f seconds, count: %d.",
Date().timeIntervalSince1970 - start, array.threadsafe.count
))
}
}
}
unsafeArray()
safeArray()
Output:
Most of the time:
experiments(31117,0x7000038d0000) malloc: *** error for object 0x11f663d28: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Sometimes:
IndexError: Index out of range
Unfortunately also:
Unsafe loop took 1.916 seconds, count: 994.
Safe loop took 11.258 seconds, count: 515.
Doesn't seem to suffice (also, it's incredibly unperformant).
The synchronization mechanism in your question, with concurrent queue and judicious use of barrier is known as the “reader-writer” pattern. In short, it offers concurrent synchronous reads and non-concurrent asynchronous writes. This is a fine synchronization mechanism. It is not the problem here.
But there are a few problems:
In the attempt to pare back the implementation, this class has become very inefficient. Consider:
class ThreadSafeArray<Element> {
private var array: [Element]
private let queue = DispatchQueue(label: "ThreadsafeArray.reader-writer", attributes: .concurrent)
init(_ array: [Element] = []) {
self.array = array
}
}
extension ThreadSafeArray {
var threadsafe: [Element] {
get { queue.sync { array } }
set { queue.async(flags: .barrier) { self.array = newValue } }
}
}
let numbers = ThreadSafeArray([1, 2, 3])
numbers.threadsafe[1] = 42 // !!!
What that numbers.threadsafe[1] = 42 line is really doing is as follows:
Fetching the whole array;
Changing the second item in a copy of the array; and
Replacing the whole array with a copy of the array that was just created.
That is obviously very inefficient.
The intuitive solution is to add an efficient subscript operator in the implementation:
extension ThreadSafeArray {
typealias Index = Int
subscript(index: Index) -> Element {
get { queue.sync { array[index] } }
set { queue.async(flags: .barrier) { self.array[index] = newValue} }
}
}
Then you can do:
numbers[1] = 42
That will perform a synchronized update of the existing array “in place”, without needing to copy the array at all. In short, it is an efficient, thread-safe mechanism.
What will end up happening, as one adds more and more basic “array” functionality (e.g., especially mutable methods such as the removing of items, adding items, etc.), you end up with an implementation not dissimilar to the original implementation you found online. This is why that article you referenced implemented all of those methods: It exposes array-like functionality, but offering an efficient and (seemingly) thread-safe interface.
While the above addresses the data races, there is a deep problem in that code sample you found online, as illuminated by your thread-safety test.
To illustrate this, let’s first assume we flesh out our ThreadSafeArray to have last, append() and make it print-able:
class ThreadSafeArray<Element> {
private var array: [Element]
private let queue = DispatchQueue(label: "ThreadsafeArray.reader-writer", attributes: .concurrent)
init(_ array: [Element] = []) {
self.array = array
}
}
extension ThreadSafeArray {
typealias Index = Int
subscript(index: Index) -> Element {
get { queue.sync { array[index] } }
set { queue.async(flags: .barrier) { self.array[index] = newValue} }
}
var last: Element? {
queue.sync { array.last }
}
func append(_ newElement: Element) {
queue.async(flags: .barrier) {
self.array.append(newElement)
}
}
}
extension ThreadSafeArray: CustomStringConvertible {
var description: String {
queue.sync { array.description }
}
}
That implementation (a simplified version of the rendition found on that web site) looks OK, as it solves the data race and avoids unnecessary copying of the array. But it has its own problems. Consider this rendition of your thread-safety test:
let numbers = ThreadSafeArray([0])
DispatchQueue.concurrentPerform(iterations: 1_000) { <#Int#> in
let lastValue = numbers.last! + 1
numbers.append(lastValue)
}
print(numbers) // !!!
The strict data race is solved, but the result will not be [0, 1, 2, ..., 1000]. The problem are the lines:
let lastValue = numbers.last! + 1
numbers.append(lastValue)
That does a synchronized retrieval of last followed by a separate synchronized append. The problem is that another thread might slip in between these two synchronized calls and fetch the same last value! You need to wrap the whole “fetch last value, increment it, and append this new value” in a single, synchronized task.
To solve this, we would often give the thread-safe object a method that would provide a way to perform multiple statements as a single, synchronized, task. E.g.:
extension ThreadSafeArray {
func synchronized(block: #escaping (inout [Element]) -> Void) {
queue.async(flags: .barrier) { [self] in
block(&array)
}
}
}
Then you can do:
let numbers = ThreadSafeArray([0])
DispatchQueue.concurrentPerform(iterations: 1_000) { <#Int#> in
numbers.synchronized { array in
let lastValue = array.last! + 1
array.append(lastValue)
}
}
print(numbers) // OK
So let’s return to your intuition that the author’s class can be simplified. You are right, that it can and should be simplified. But my rationale is slightly different than yours.
The complexity of the implementation is not my concern. It actually is an interesting pedagogical exercise to understand barriers and the broader reader-writer pattern.
My concern is that (to my point 3, above), is that the author’s implementation lulls an application developer in a false sense of security provided by the low-level thread-safety. As your tests demonstrate, a higher-level level of synchronization is almost always needed.
In short, I would stick to a very basic implementation, one that exposes the appropriate high-level, thread-safe interface, not a method-by-method and property-by-property interface to the underlying array, which almost always will be insufficient. In fact, this desire for a high-level, thread-safe interface is a motivating idea behind a more modern thread-safety mechanism, namely actors in Swift concurrency.
I suspect this line is your issue:
DispatchQueue.global().sync { ...
If you specify one serial queue you want to use here you should get the result you want.
Something like:
let array = SynchronizedArray<Int>()
var iterations = 1000
let queue = DispatchQueue(label: "queue")
DispatchQueue.concurrentPerform(iterations: 1000) { index in
array.append(array.last ?? 0)
queue.sync {
iterations -= 1
if iterations == 0 {
print(array.count)
}
}
}
Another method of locking objects is:
func lock(obj: AnyObject, work:() -> ()) {
objc_sync_enter(obj)
work()
objc_sync_exit(obj)
}
Could your class use this to lock its standard array when needed?
I have the following code:
public fun findSomeLikeThis(): ArrayList<T>? {
val result = Db4o.objectContainer()!!.queryByExample<T>(this as T) as Collection<T>
if (result == null) return null
return ArrayList(result)
}
If I call this like:
var list : ArrayList<Person>? = p1.findSomeLikeThis()
for (p2 in list) {
p2.delete()
p2.commit()
}
It would give me the error:
For-loop range must have an 'iterator()' method
Am I missing something here?
Your ArrayList is of nullable type. So, you have to resolve this. There are several options:
for (p2 in list.orEmpty()) { ... }
or
list?.let {
for (p2 in it) {
}
}
or you can just return an empty list
public fun findSomeLikeThis(): List<T> //Do you need mutable ArrayList here?
= (Db4o.objectContainer()!!.queryByExample<T>(this as T) as Collection<T>)?.toList().orEmpty()
try
for(p2 in 0 until list.count()) {
...
...
}
I also face this problem when I loop on some thing it is not an array.
Example
fun maximum(prices: Array<Int>){
val sortedPrices = prices.sort()
for(price in sortedPrices){ // it will display for-loop range must have iterator here (because `prices.sort` don't return Unit not Array)
}
}
This is different case to this question but hope it help
This can also happen in Android when you read from shared preferences and are getting a (potentially) nullable iterable object back like StringSet. Even when you provide a default, the compiler is not able to determine that the returned value will never actually be null. The only way I've found around this is by asserting that the returned expression is not null using !! operator, like this:
val prefs = PreferenceManager.getDefaultSharedPreferences(appContext)
val searches = prefs.getStringSet("saved_searches", setOf())!!
for (search in searches){
...
}