Algorithm
回文排列
class Solution {
public boolean canPermutePalindrome(String s) {
if (s == null || s.length() <= 1) {
return true;
}
Set<Character> set = new HashSet<>();
for (int i = 0; i < s.length(); i++) {
if (set.contains(s.charAt(i))) {
set.remove(s.charAt(i));
} else {
set.add(s.charAt(i));
}
}
if (set.size() >= 2) {
return false;
}
return true;
}
}
Review
Why I quit Android Development after 10 years and what I plan to do now
作者身为一个10年的Android开发者,要转后端开发。
讲了Android技术的更新换代太快,厌烦深入UI和业务,并说了Flutter H5等可替代性,认为Android已经是暮年,随时可能会消失。
聊公司里的架构师和主管等不可能由纯粹的Android开发者担任,并且不具备这样的技术。
The sad truth is that the path of an architect or principal / staff engineer is closed for a pure android developer. The pure android dev simply won’t have the required skills to fulfill those positions. It was a great ride for me, but I’ll never work on a project as android developer again.
-------
个人还是觉得作者说的还是有道理的,而且很幸运可以转到后端开发,但是在中国这个行情,35岁中年危机,普通的开发者就算转后端又有什么用呢,目前也是只能走一步看一步,平时多学习吧。
Tip
这周学习深入理解Kotlin协程,看到两个关键字,tailrec和inline,学习记录一下:
tailrec
tailrec用于声明一个函数是尾递归函数(tail-recursive function)。尾递归函数是一种特殊的递归函数,它的最后一个操作是一个递归调用,这样编译器可以将它优化为一个迭代循环,从而避免递归调用带来的栈溢出问题。
使用tailrec关键字可以让编译器对函数进行优化,从而提高代码的性能和安全性。如果函数不是尾递归函数,使用 tailrec 关键字会导致编译错误。示例如下:
tailrec fun factorial(n: Int, acc: Int = 1): Int {
return if (n == 0) acc else factorial(n - 1, acc * n)
}
在上述代码中,我们定义了一个尾递归函数 factorial,用于计算一个整数的阶乘。函数接受两个参数,n 表示要计算阶乘的整数,acc 表示当前的阶乘值。在函数内部,我们使用一个递归调用来计算阶乘,并在递归调用的最后一个操作中使用 return 语句返回结果。由于函数是尾递归函数,因此我们可以使用 tailrec 关键字来声明它。
需要注意的是,只有在函数的最后一个操作是一个递归调用时,才能使用 tailrec 关键字。如果函数的最后一个操作不是一个递归调用,或者递归调用不是函数的最后一个操作,编译器将无法将其优化为一个迭代循环,从而可能导致栈溢出问题。
tailrec 关键字是 Kotlin 中一个非常有用的特性,它可以帮助我们写出更加高效、安全的递归函数。
inline
inline 用于声明一个函数是内联函数(inline function)。内联函数是一种特殊的函数,编译器在编译代码时会将函数的代码段直接复制到调用它的地方,而不是像普通函数那样将函数的调用作为一个独立的函数调用栈来执行。这种直接复制的过程类似于 C/C++ 中的宏展开(macro expansion),它可以带来一定的性能优势,同时也可以避免函数调用带来的额外开销。
使用 inline 关键字可以让编译器对函数进行内联优化,从而提高代码的性能。不过,由于内联函数的代码段会被复制到调用它的地方,因此内联函数的代码大小不能太大,否则会导致编译后的代码过大,从而影响程序的性能和可读性。示例如下:
inline fun <reified T> Iterable<*>.filterIsInstance(): List<T> {
return filter { it is T }.map { it as T }
}
在上述代码中,我们定义了一个内联函数 filterIsInstance,用于从一个 Iterable 中过滤出指定类型的元素,并返回一个对应的 List。函数使用了泛型类型 T,以便在调用时指定过滤的元素类型。在函数内部,我们使用了标准库函数 filter 和 map 来实现过滤操作,并在函数返回值中使用了类型转换。
需要注意的是,由于内联函数的代码段会被复制到调用它的地方,因此内联函数中不允许使用一些不支持内联的语法,比如函数类型的参数和返回值、非局部返回、重复的变量名称等。此外,内联函数的参数会被复制到调用它的地方,因此不应该将大对象或者不可变的对象声明为内联函数的参数。
在使用inline的例子里,用到了reified,这里了解下这个关键字。
reified
reified 用于修饰泛型类型参数,表示该参数在运行时可以被具体化(reified type parameter)。具体化泛型类型参数意味着可以在运行时访问和操作泛型类型的信息,而不仅仅是在编译时。
使用 reified 关键字可以帮助我们编写更加灵活和通用的代码。例如,可以使用具体化泛型类型参数来实现类型安全的类型转换、动态创建泛型类型的实例、访问泛型类型的成员等。示例如下:
inline fun <reified T> createInstance(): T {
return T::class.java.newInstance()
}
在上述代码中,我们定义了一个内联函数 createInstance,用于创建一个指定类型的实例。函数使用了泛型类型参数 T,并在函数内部使用了 T::class 表达式来访问泛型类型的 Class 对象,并使用 newInstance() 方法来创建实例。由于使用了 reified 关键字,编译器会对 T 进行具体化,从而允许我们在运行时访问泛型类型的信息。
需要注意的是,reified 关键字只能用于内联函数中的泛型类型参数,因为只有内联函数才能在编译时获得泛型类型的信息。此外,由于具体化泛型类型参数会增加程序的复杂性和内存消耗,因此需要谨慎使用 reified 关键字,并根据具体的场景进行选择。