从数组开始
被new出的未被赋值的对象数组中的每个元素值为null,而基本数字类型则是0。
通常意义上的数组是不可变大小,并且类型相同的
Containers
- Collection
- List
- Set
- Map
Java编程中拿到的永远是指针,没有拷贝构造的概念
遍历的方法
所有的容器都可以用于for each循环
for(Stu s:lst){
s.incr(); //1
s = new Stu(); //2
}
上述代码中,1起作用而2不起作用,这说明for each循环中能改变的是对象内的内容,而不能是对象本身
foreach的实现,其实就是编译器的语法糖,将for each语句转换为iterator的使用。
List
List与Array的互相转化:
两种toArray:
Object[] toArray()
<T> T[] toArray(T[] a)
其中前者是不安全的(因为返回的是Object数组),后者将会把结果写入传入的数组。但如果传入的数组的大小不足,则会生成一个新的数组。
Integer[] array = list.toArray(new Integer[list.size()]);
Integer[] array = list.toArray(Integer[]::new);
arr->list:
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);
这样的写法只能转换成list,至于list的各种实现需要调用其构造函数
List是接口,两个经典的实现:
ArrayList
一些实现细节:
- 默认size为10
- 传入大小为0的构造函数和默认构造函数的所有ArrayList分别都对应了1个static的空数组,可节省空间
- grow函数源码如下:
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
可以看到,增长量的期望值是oldCap的2倍,但要满足最小增长长度
无参的grow调用grow(size+1)
- asd
LinkedList
是双向链表实现的,可以当做Queue和Deque(也即也有stack的功能)来做
Set
常用的两种实现
HashSet
TreeSet
部分小结
List和Set的使用性能对比
整个容器的结构图
Map
是key value组成的一对值
Java使用RTTI,c++的模板是预编译的,前者的泛型对象都是属于同一个类的,而c++不然
泛型相关
如果Foo是Bar的子类,G<Foo>不是G<Bar>的子类
通配符:wildcard
带尖括号的泛型类不能用于instanceof的右操作数,因为是类本身,而不是尖括号确定的泛型类作为类型的识别基础