今天看公众号的时候,看到占小狼公众号有篇文章叫Java中的坑,然后就点进去看了,我先罗列一下问题:
第一个坑:
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 100; i++) {
set.add(i);
set.remove(i-1);
}
System.out.println(set.size());
第二个坑:
package com.example.demo.chapter3;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Set<Short> set = new HashSet<>();
for (short i = 0; i < 100; i++) {
set.add(i);
set.remove(i-1);
}
System.out.println(set.size());
}
}
上面两个坑,问你输出结果是什么?
第三个坑:
package com.example.demo.chapter3;
public class TestTwo {
public static void main(String[] args) {
Object i = 1 == 1? new Integer(3):new Double(1);
System.out.println(i);
}
}
第4个坑:
Map<Long,String> map = new HashMap<>();
map.put(1L,"1111");
map.put(2L,"2222");
map.put(3L,"3333");
long[] ids = ArrayUtils.toPrimitive((Long[]) map.keySet().toArray());
其中ArrayUtils.java
是commons-lang3.jar
中的一个工具类,你只需要在你的pom.xml
中添加下面的依赖就可以:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
结果抛出了异常:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Long;
at com.example.demo.chapter3.ExceptionTest.main(ExceptionTest.java:14)
第一个坑的答案是:1
为什么是1,原因在于remove
方法,set
的remove
方法源码如下所示:
/**
* Removes the specified element from this set if it is present
* (optional operation). More formally, removes an element <tt>e</tt>
* such that
* <tt>(o==null ? e==null : o.equals(e))</tt>, if
* this set contains such an element. Returns <tt>true</tt> if this set
* contained the element (or equivalently, if this set changed as a
* result of the call). (This set will not contain the element once the
* call returns.)
*
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if this set contained the specified element
* @throws ClassCastException if the type of the specified element
* is incompatible with this set
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this set
*/
boolean remove(Object o);
- 该方法的参数是一个对象
Object
类型 - 从set集合中移除某个具体的元素
坑1的答案和解析
从第一个坑的代码可以看到,当i=0
的时候,同时向set
集合中添加0元素,然后移除-1元素;依次类推,那么当i=1
的时候,set集合中只有1,0元素会被移除掉,所以最后就只剩下一个元素99。
需要注意的是remove
的参数,在不同类中存在重载方法,有的参数是下标索引,有的是元素对象,所以调用不同的方法传入不同的参数,得到的结果也是不同的。
坑2的答案和解析
第二个与第一个不同之处便是,数据类型是Short
,但是这个结果就不一样的,最后打印出来的结果是100,为什么会这样子?同一段代码,只是数据类型的改变,导致最后的结果不一样。
上图是第二个坑的字节码文件,直接使用javap -c 类名
的方式得到了该文件的字节码文件,它在set
集合中装入的是Short
类型的数据,但是remove
的时候,它却是remove
的Integer
对象,因为Short
和Integer
类型都重写了Object
类的equals
方法,所以在remove
的时候作比较,最后得到的结果就是false
,set
集合中的元素都没有被移除掉。
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
public boolean equals(Object obj) {
if (obj instanceof Short) {
return value == ((Short)obj).shortValue();
}
return false;
}
第三个坑的答案与解析
答案是3.0
,不是你想的3。
这个原因我也是通过字节码看出来的,因为前面是一个恒真的条件,然后使用了三目运算符,本来应该返回new Integer(3)
的结果3,但是实际却返回了3.0,问题的本质在于三目运算符存在隐式的类型转换。所以返回了3.0。字节码如下:
需要注意的是里面有个i2f
,表示将int
类型转为float类型,关于这些指令是什么意思,可以去字节码指令列表去对照看每个指令的意思JVM 虚拟机字节码指令表
第四个坑的答案与解析
/**
* Returns an array containing all of the elements in this set; the
* runtime type of the returned array is that of the specified array.
* If the set fits in the specified array, it is returned therein.
* Otherwise, a new array is allocated with the runtime type of the
* specified array and the size of this set.
*
* <p>If this set fits in the specified array with room to spare
* (i.e., the array has more elements than this set), the element in
* the array immediately following the end of the set is set to
* <tt>null</tt>. (This is useful in determining the length of this
* set <i>only</i> if the caller knows that this set does not contain
* any null elements.)
*
* <p>If this set makes any guarantees as to what order its elements
* are returned by its iterator, this method must return the elements
* in the same order.
*
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
*
* <p>Suppose <tt>x</tt> is a set known to contain only strings.
* The following code can be used to dump the set into a newly allocated
* array of <tt>String</tt>:
*
* <pre>
* String[] y = x.toArray(new String[0]);</pre>
*
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
*
* @param a the array into which the elements of this set are to be
* stored, if it is big enough; otherwise, a new array of the same
* runtime type is allocated for this purpose.
* @return an array containing all the elements in this set
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in this
* set
* @throws NullPointerException if the specified array is null
*/
<T> T[] toArray(T[] a);