1、将数组转换为ArrayList
将数组转换为ArrayList,程序员们通常会这么做:
List<String> list = Arrays.asList(arr);
如果你这么写的话Arrays.asList()将会返回一个ArrayList,但是这个ArrayList是Arrays这个类内部的一个静态类而非我们最经常使用的java.util.ArrayList这个包下面的ArrayList。Arrays这个类下面的ArrayList拥有set方法,get方法和contain方法,但是没有添加的方法,因此它的大小是固定的。如果我们想创建一个真正的ArrayList其操作方法如下:
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));
ArrayList
的构造器可以接受集合类型的数据,集合类型的数据java.util.Arrays.ArrayList的父类。
2、检查数组是否包含值
程序员经常做的:
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);
这个代码是有效的,但是没有必要先将一个列表转为一个集合。这么做会额外的消耗时间。我们可以这样简化代码:
Arrays.asList(arr).contains(targetValue);
或者:
for(String s: arr){
if(s.equals(targetValue))
return true;
}
return false;
第一种写法的可读性要高于第二种写法。
3、从循环内的列表中删除元素
思考以下在迭代期间删除元素的代码:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (int i = 0; i < list.size(); i++) {
list.remove(i);
}
System.out.println(list);
它的输出结果是:
[b, d]
这个方法中有一个严重的问题。当一个元素被移除时,列表的大小会减少但是索引值会增加。因此如果你在循环中使用索引删除元素,那么你不会得到正确的结果。
你可能知道在循环中使用迭代器是正确的写法,而foreach和迭代器的原理基本相同,于是乎,你写出了如下代码:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (String s : list) {
if (s.equals("a"))
list.remove(s);
}
但是如果你这么写的话,它会抛出ConcurrentModificationException异常。
正确的写法如下:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.equals("a")) {
iter.remove();
}
}
.next()
必须放在.remove()之前。在foreach循环中编译器将会把.next()放在之后,这将会引发ConcurrentModificationException异常。你可以查看 ArrayList.iterator()的源码,查看更多信息。
4.、Hashtable vs HashMap
在算法的规则中Hashtable 只是一种数据结构的名称。但是在java中数据结构的名称是HashMap。
Hashtable 和HashMap最重要的区别是Hashtable
是线程安全的。但是你不会经常使用Hashtable,你最经常使用的是
HashMap。
5、使用原始集合类型
在Java中,原始类型和无界通配符类型很容易混合在一起。以Set 为例子,Set 是元素类型,Set<?>是无限制的通配符类型。
思考以下代码,以下代码使用了原始的List
作为参数:
public static void add(List list, Object o){
list.add(o);
}
public static void main(String[] args){
List<String> list = new ArrayList<String>();
add(list, 10);
String s = list.get(0);
}
这个代码将会收到如下错误:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at ...
使用原始集合类型是十分危险的,因为元素集合类型会跳过泛类检查,这是十分不安全的。Set
, Set<?>
, 和 Set<Object>之间存在着巨大的差异。
6、访问级别
许多程序员经常把类的属性设置为public 。通过直接引用,你可以很方便的获取到属性值,但是这是非常糟糕的一种设计。根据经验,我们应该外部访问者较低的权限。
7、ArrayList vs LinkedList
当程序员不知道 ArrayList
和 LinkedList之间的区别的时候,他们会经常使用ArrayList
,因为它更为熟悉。但是ArrayList
和 LinkedList之间却存在巨大的性能差异。简而言之,LinkedList的优势在于添加,删除和修改,而ArrayList
的优势在于查询。
8、Mutable vs Immutable
不可变对象拥有许多优点,例如:简单,安全等。但它要求每个不同的值都有一个单独的对象,对象太多可能会导致垃圾回收的消耗过高。mutable 和 immutable之间需要权衡。
通常,可变对象用于避免生成过多的中间对象。一个经典的例子是串联大量字符串。如果使用不可变字符串,则会生成许多可以立即进行垃圾回收的对象。这在CPU上浪费了时间和算力,使用可变对象是正确的解决方案。
String result="";
for(String s: arr){
result = result + s;
}
还有其他需要可变对象的情况。 例如,通过将可变对象传递到方法中,可以收集多个结果,而无需跳过太多的语法环。另一个例子是排序和过滤:当然,您可以创建一个方法来获取原始集合,并返回一个已排序的集合,但对于较大的集合来说,这将变得极其浪费资源。
9、继承构造器错误
class Super {
String s;
public Super(String s) {
this.s = s;
}
}
public class Sub extends Super {
int x = 200;
public static void main(String[] args){
Sub s = new Sub();
如果你想上面这样写代码,编译将会报错,提示你默认继承的构造器没有被定义。在Java中,如果一个类没有定义构造函数,编译器将在默认情况下为该类插入一个默认的无参数构造函数。如果构造函数是在父类中定义的,在上面案例中,编译器不会插入默认的无参数构造函数。这是上述父类的情况。
Sub 这个类既没有无参构造器也没有有参构造器,它将会继承父类的无参构造器。但是此时父类中的无参构造器已经被有参构造器覆盖了,因此会抛出为定义构造器的错误。解决这个问题可以在父类中添加无参构造器,或者移除父类的有参构造器,再或者在子类中写明从父类继承的构造器。
10、" "或者构造器?
字符串可以被一下两种方式创建:
//1. 使用双引号
String x = "abc";
//2. 使用构造器
String y = new String("abc");
它们之间有什么不同吗?
下面的代码可以给你答案:
String a = "abcd";
String b = "abcd";
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True