今天在github上面看到了一个很好的项目,是在Stack Overflow上面点赞数top100的关于Java的回答,看了一些,感觉十分实用,对于新手来说,能加深对Java的认识,同时也能让代码更简洁。所以想写下这系列的博文,每个回答我都会亲自用代码检验一下,下面一起来看看这些小技巧吧,绝不会让你失望。
1.Java +=操作符实质
看了这个是不是觉得这有啥难的,不就是一个加法吗?但还真不是这么简单。
可以试试在编辑器中输入以下代码
int a = 5;
double b = 10.5;
a = a + b; // 这行编译出错
a += b; // 这行没有问题
a = a + (int)b; //这行也没问题
是不是感觉有点惊讶?a = a + b;不能通过,a += b;怎么就能通过了呢?其实再看这个回答之前,我也一直以为a += b其实就等同于a = a + b;然后事实明显不是这样。那这是什么原因呢?看看官方文档的解释就知道了。
意思就是对于E1 op= E2这样的表达式来说,等同于(T)(E1 op E2)。op=诸如+=、-=、*=、/=这样的运算符,E1和E2表示两个操作数,T表示E1的数据类型。那么我们上面的代码中a += b;就变成了(int)(a + b);
2.将InputStream转化为String
使用apache库我就不说了,因为我也没用过,我们就看看自己写一个函数怎么搞。
/**
* 将InputStream转化为String
* @param is
* @return
*/
static String convertStreamToString(InputStream is) {
@SuppressWarnings("resource")
// \A表示文件的开头
Scanner read = new Scanner(is).useDelimiter("\\A");
return read.hasNext() ? read.next() : "";
}
写个主函数测试一下
FileInputStream fis = new FileInputStream("test.txt");
System.out.println(convertStreamToString(fis));
完全没毛病,大家可以自己试试。
3.将数组转化为List
相信有挺多人和我一样,要么用循环,遍历数组把数组的值一个一个赋值给List。。。这是最傻逼的方法。再者,就是使用Arrays.asList()方法。用这个方法的确没毛病,但却是有坑滴。
- 这样生成的List是定长的,意味着你不能进行remove和add操作,不然会抛出UnsupportedOperationException。(好吧,难怪我说以前怎么用这个方法总报错,现在终于找到答案了)
- 如果修改数组的值,则List中的值也会变。
之所以会出现这样的情况,是因为Arrays.asList()方法返回的是Arrays的内部静态类,而不是Java.util.ArrayList的类。这个java.util.Arrays.ArrayList有set(),get(),contains()方法,但是没有任何add() 方法,所以它是固定大小的。
如果你希望避免这两个坑,请使用下面的方式
Collections.addAll(arraylist, array);
新技能get。我们来看看代码
Integer[] a = new Integer[]{1, 2, 3};
List<Integer> r = new ArrayList<Integer>();
r = Arrays.asList(a);
//Collections.addAll(r, a);
//r.add("d");
//a[0] = 10;
System.out.println(r);
这里需要提醒的是,如果你是基本类型比如int类型的数组,也应该使用Integer来进行包装,不然会报错。
4.Map遍历
在Java中有多种遍历HashMap的方法。让我们回顾一下最常见的方法和它们各自的优缺点。由于所有的Map都实现了Map接口,所以接下来方法适用于所有Map(如:HaspMap,TreeMap,LinkedMap,HashTable,etc)
方法一:使用for-each迭代entries
这是最常见的方法,并在大多数情况下更可取的。当你在循环中需要使用Map的键和值时,就可以使用这个方法
for(Map.Entry<Integer, String> entry:map.entrySet()) {
Integer key = entry.getKey();
String value = entry.getValue();
}
注意:For-Each循环是Java5新引入的,所以只能在Java5以上的版本中使用。如果你遍历的map是null的话,For-Each循环会抛出NullPointerException异常,所以在遍历之前你应该判断是否为空引用。
方法二:使用for-each迭代keys和values
如果你只需要用到map的keys或values时,你可以遍历KeySet或者values代替entrySet
for(Integer i:map.keySet()) {
System.out.println(i);
}
for(String s:map.values()) {
System.out.println(s);
}
这个方法比entrySet迭代具有轻微的性能优势(大约快10%)并且代码更简洁
方法三:使用Iterator迭代
使用泛型
Iterator<Map.Entry<Integer, String>> entries = r.entrySet().iterator();
while(entries.hasNext()) {
Map.Entry<Integer, String> entry = entries.next();
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key + ":" + value);
}
不使用泛型(算了,还是推荐大家使用泛型吧。。。)
你可以使用同样的技术迭代keyset或者values
这个似乎有点多余但它具有自己的优势。首先,它是遍历老java版本map的唯一方法。另外一个重要的特性是可以让你在迭代的时候从map中删除entries的(通过调用iterator.remover())唯一方法.如果你试图在For-Each迭代的时候删除entries,你将会得到unpredictable resultes 异常。
从性能方法看,这个方法等价于使用For-Each迭代
方法四:迭代keys并搜索values(低效率,好吧,我就是一直用的这种低效率的方法)
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
这个方法看上去比方法#1更简洁,但是实际上它更慢更低效,通过key得到value值更耗时(这个方法在所有实现map接口的map中比方法#1慢20%-200%)。如果你安装了FindBugs,它将检测并警告你这是一个低效的迭代。这个方法应该避免。
总结
如果你只需要使用key或者value使用方法#2,如果你坚持使用java的老版本(java 5 以前的版本)或者打算在迭代的时候移除entries,使用方法#3。其他情况请使用#1方法。避免使用#4方法。
5.如何测试一个数组中是否包含指定的值
excuse me???这么简单,不就是一个循环遍历吗??
简单而优雅的方法(好,这个技能掌握了)
String[] values = {"A", "B", "C", "D"};
if(Arrays.asList(values).contains("A")) {
System.out.println("6666");
} else {
System.out.println("5555");
}
自己写逻辑
问题的本质,其实是一个查找的问题,即查找一个数组是否包含某个值。对于原始类型,若是无序的数组,可以直接写一个 for 循环:
for(String s:values) {
if(s.equals("A")) {
return true;
} else {
return false;
}
}
若是有序的数组,可以考虑二分查找或者其他查找算法:
public static boolean useArraysBinarySearch(String[] arr, String targetValue) {
int a = Arrays.binarySearch(arr, targetValue);
if(a >= 0)
return true;
else
return false;
}
若数组里包含的是一个个对象,实际上比较就是引用是否相等(String 类型是判断 值是否相等),本质就是比较 hashcode 和 equal 方法,可以考虑使用 List 或者 Set,如下
public static boolean useList(String[] arr, String targetValue) {
return Arrays.asList(arr).contains(targetValue);
}
for(String s:values) {
if(s.equals("A")) {
return true;
} else {
return false;
}
}
好了,今天先写5个问题,去吃饭了。越写越有意思,本渣渣表示很惊奇啊,欲知后事如何,请看明天的博文。