2

1、转化数组为ArrayList

通常开发者转化数组为ArrayList的方式为

List<String> list = Arrays.asList(arr);

Arrays.asList()会返回一个ArrayList,而这个ArrayList是Arrays类的静态内部类,不是java.util.ArrayList。

这个类有get()、set()和contains()方法,但却没有任何可以添加元素的方法。正确的做法可以这样做

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

2、检查数组里面是否包含某个元素

部分开发者会这样实现

return new HashSet<String>(Arrays.asList(arr)).contains(targetValue);

结果是对的,但是没有必要转化为Set,这反而会花费更多时间,可以简单这样实现

return 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]

因为当数组删除一个元素后,它的长会缩小,index相当于向后移动一位,这是个严重的问题。当你想通过index来删除多个元素时候,这种方法是不可取的。

你也许知道用迭代器来删除是没问题的,并且java中有一类for语句原理就是使用迭代器。但实际你想用这类for语句来代替迭代器进行删除也是不行的,如下代码

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()之前被调用。而在for循环中,编译器会在元素被remove之后调用next(),因此就会抛出ConcurrentModificationException异常。

4、hashtable和hashmap

java中有两类,HashTable和HashMap,两者的数据结构是一致的(哈希表),然后两者的区别是:HashTable是同步的。

所以HashTable是线程安全的,HashMap不是线程安全的。

提示:也可以使用ConcurrentHashMap来保证线程安全,ConcurrentHashMap使用分段锁(segment)的原理,效率上会高一些。

5、集合中原生态类型(raw type)的使用

在java中,开发者通常把原生态类型(raw type)通常和无界通配符类型(unbounded wildcard type)弄混。拿Set来举例子,Set是原生态类型,而Set<?>是无界通配符类型。

如下代码使用了原生态类型

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 ...

原生态类型会越过泛型的校验,是不安全的。

6、访问级别

有的开发者将类某些属性直接定义为public,这很容易通过外部访问,但绝对是很差的设计。根据拇指规则,最佳做法应该是尽量减少属性的访问级别。

7、LinkList vs ArrayList

很多开发者都习惯使用ArrayList,可能ArrayList相对来说比较熟悉的缘故,其实ArrayList和LinkList还是存在很大的区别。简而言之,LinkList应该在有大量增删操作且无随机访问操作时候使用。

8、可变(mutable) vs 不可变(immutable)

不可变对象有很多优点,比如简单、安全等。但对于多个值则需要多个不同的对象来表示,对象过多时,会消耗很多的GC资源。

通常的,可变对象可用来避免产生过多的对象。如下代码中使用了不可变对象,那么执行过程中将会产生很多的String,消耗很多时间和cpu性能。如果换成可变对象(StringBuilder等),将会好很多。

String result="";
for(String s: arr){
    result = result + s;
}

9、父子类的构造函数

    class Super {
        String s;

        public Super(String s) {
            super();
            this.s = s;
        }

    }

    class Sub extends Super {

        public Sub(String s) {
        }

        public Sub() {

        }
    }

上述代码会出错,是因为父类默认构造函数没有定义。在java中,如果一个类没有定义构造函数,则编译器会给它构造默认的无参构造函数。如果类中定义了构造函数,那么编译器将不会给它插入默认构造函数。这个正是上述父类的遇到的情况。

在子类中的两个构造函数中,编译器试图插入父类的默认构造函数super(); ,然而并未找到,因此编译出错。

10、"" or Constructor

字符串可以通过两种方式建立

//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

小黑胖子
22 声望3 粉丝

« 上一篇
java类型擦除