今天在用java写一个程序的时候,遇到了一个小问题。写出来,一起分享。
由于写的是一个简单的一段代码,并不打算长期使用,所以我用set的过滤能力,对一个较大的数组进行过滤,去掉重复的项,可是遇到了问题。
出问题部分的主要代码:
int n=cin.nextInt(); int a[]=new int[n]; for(int i=0;i<n;i++){ a[i]=cin.nextInt();
} Set set = new HashSet(Arrays.asList(a)); Iterator it=set.iterator(); while(it.hasNext()){ System.out.println(it.next()); }
假设输入的为一段长度的数字:10 2 49 89 123 2 19 10 89
当这样写的时候,总是出现输出:[I@1270b73 这显然是对象地址。。。
下断点得到set集合的各种属性:
查看器table映射情况:
大家可以看到,作为初值15个位置,只映射了一个位置,本应映射更多位置的。
其将数组当成一个对象,直接映射了:
由此,我记得以前使用String[]数组的时候就没有出现这种情况,因此使用String[]将原程序改了,立刻就得到了相关的7个映射位置,实现了所需要的功能,当然中间多了些转换,然后回过头来看看这里到底发生了什么。
为什么会出现这种情况?
首先,想到的是String本身就被当做对象对待,而int被当做基本类型对待,这两个是有区别的。因此问题可能出现在这里。
那么对于int来讲,他的包装类为Integer,更改一下,试一下。
当将int a[]=new int[n];改为Integer a[]=new Integer[n];就好了:
int n=cin.nextInt();
Integer a[]=new Integer[n];
for(int i=0;i<n;i++){
a[i]=cin.nextInt();
}
Set set = new HashSet(Arrays.asList(a));
Iterator it=set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
再次下断点,查看相关属性:
查阅文档:
Arrays.asList()的文档:
asList
public static <T> List<T> asList(T... a)
-
Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.) This method acts as bridge between array-based and collection-based APIs, in combination with
Collection.toArray. The returned list is serializable and implements
RandomAccess
.This method also provides a convenient way to create a fixed-size list initialized to contain several elements:
List stooges = Arrays.asList("Larry", "Moe", "Curly");
-
- Parameters:
-
a
- the array by which the list will be backed. - Returns:
- a list view of the specified array.
- See Also:
-
Collection.toArray()
并没有得到什么实质性有用的信息,只是了解了这个方法的一些特性。
通过在工作空间中追源码得到:
public static <T> List<T> asList(T... a) { return new ArrayList<T>(a); }
我们看到这里定义了泛型,也就是说传进来的东西的对象类型被隐性接受。
继续找ArrayList<T>(a)
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { if (array==null) throw new NullPointerException(); a = array; }
在此之前,我没认真看过这类的代码,泛型在这里起了作用,当你传入int[] a=new int[n];没注意都,int就进去了
由上,我们得知,数组的基础类型(int/Integer)通过我们调用asList方法的时候,已经进入到ArrayList之中,并返回给我们调用的左部。
又基于Set set = new HashSet(Arrays.asList(a));我使用的是HashSet,那么HashSet在映射的时候,需要一个对象的hashcode,那么如果这个传入的类型不能够提供hashcode会怎么样呢?
实验:
在int a[]=new int[n];为数组下,我们都知道a[i]不能够调用.hashCode()方法,因为没有包装,没有这个方法。
在Integer a[]=new Integer[n];为数组下,我们可以轻松调用.hashCode()方法,因为其为包装类,有该方法。
那么我们就可以思考,为什么会出现那种情况了,因为java根据泛型接收的外部参数的类型,然后查看这个外部参数是否有.hashCode()方法,如果有则调用,用于映射(在内部机制中,我认为这里使用的接口,查看其是否实现了这类接口),如果没有这个方法(就像这个int基础类型),则追溯其传入参数的更大范围的对象(在这里是数组对象),这里将数组当成一个对象映射到桶中(如下图)。
学校运动会放假,明天去华山旅游,所以这篇日志不能够接着写下去,赶紧睡觉,但是具体的意思已经表达出来了,下一篇文章会接着这个问题从实现方面思考,来对java面向对象带来的方便和麻烦作一个自己的思考总结。
论证期间的一些思考和证明:
int c=3;
对于c这个基础类型是没有任何方法可以调用的,因为其不是对象。
可是对于int a[]=new int[n]; Integer b[]=new Integer[n];
a.clone();是可以执行的,而且查看列表,与b数组的方法是一样的。
这是数组作为一种“容器”,所具有的方法和相关性质。
所以可以认为int数组形式下,数组时被看做对象来处理的,而对象都有了方法。但是这两个不同类型数组的基类是不一致的。
进一步探究》》
对于不同的类型的数组:
int n=cin.nextInt(); int a[]=new int [n]; Integer b[]=new Integer[n]; for(int i=0;i<n;i++){ a[i]=cin.nextInt(); } Set set = new HashSet(Arrays.asList(a)); System.out.println("int型数组的类的名字:"+a.getClass().getName()); System.out.println("Integer型数组的类的名字:"+b.getClass().getName());
得到结果为:
int型数组的类的名字:[I
Integer型数组的类的名字:[Ljava.lang.Integer;
这样可以看出这两个数组的基类是不同的,这样做似乎多余。但严格证明了这点。
对于同样是基类的char进行一步测试:
char c[]=new char[n];
System.out.println("char型数组的类的名字:"+c.getClass().getName());
方法同上,结果为:
char型数组的类的名字:[C
又出来一个[C
可是即使一个类也好,传进去按照泛型,不至于说所有的方法都失效了啊。而且只要是基础类型进去后,length都变成了1。也就是说当成了单个对象进去了。
动用调试:
发现,在使用int[] a=new int[n];时候,查看set的hashmap的值只有单个不为null(hashmap初始为15,只映射了1个位置),
同样测试Integer数组的时候,发现,set的hashmap的值全部被映射。
得出结论:
对于这两个被传进来的数组,由于类型不同,被用作不同的处理方式,int数组的整个数组被当做一个对象,进行映射,而Integer数组的每一个值均被当做一个对象用来映射。
对于int值他们被当做了整体。