数组:可以存放primitive类型的变量和对象
容器:对象的容器,不能 放primitive变量,当然可以装箱
8.1 数组
8.2 容器概论
8.3 List之ArrayList
8.4 List之 Vector
8.5 List之LinkedList
8.6 HastSet和TreeSet
8.7 HashMap
8.8 HashTable
8.9 WeakHashMap
8.1 数组
——java数组实际上也是一种引用类型,这个类型的名字就是int[]
——但是new的时候有一点不一样
int[] arr = new int[10];
T t = new T();
就这么点不一样的地方
1、数组的定义:
——int[] arr1; //这只是定义了一个对数组的引用,所以这里不能指定大小
2、数组的存储空间分配:
(1)花括号:事先知道有几个元素了
——int[] arr2 = {1,2,3,4,5}; //创建了一个有5个元素的数组
(2)new:事先不知道有几个元素
——int[] arr2 = new int[number];
——元素个数可以在运行时确定,所有基本数据类型的元素初始值都是0(0对应于各种类型的初始值),所有对象的元素初始值都是NULL
——但是数组的元素个数不能改变
(3)如果元素类型不是基本数据类型,则必须使用new
——每个元素都是一个引用
——未对对象元素赋值之前,它是null
3、数组的相互赋值:
——int[] arr3; arr3 = arr2; ///实际上指向了同一块内存,这只是引用的赋值
4、数组的大小:
——int size = arr3.length;
——最大索引数是:arr3.length-1,因为数组下标从0开始
5、数组越界异常:
——下标越界对于java来说会在运行时抛出异常
——但是对于C++来说,它允许你访问越界的内存,只是不保证行为正确,必定是错的
6、多维数组:
——这里的多维数组好理解,对于二维数组来讲,每一行都是一个一维数组,有几列就表示一维数组中有几个元素
——int[][] arr1 = { {1,2,3,},{4,5,6} };
——arr1.length表示有几个一维数组(行数), arr1[0].length表示第一行作为一个一维数组,有几个元素,每一行的元素数可能是不同的。
——int[][] arr2 = new int[2][3]; //表示有2行,每一行最多有3个元素
——对于3维数组来讲,每一行都是一个二维数组。
创建对象数组:
AA[][] arr; //声明了一个对二维数组的引用了
arr = new AA[2][]; //指明这个数组有2行,即下面还有2个一维的数组等你定义
for(int i=0; i<arr.length; ++i){
arr[i] = new AA[3];
//对于每一行,都是一个数组,这里是1维数组,有3个元素
arr[i][0] = new AA();
arr[i][1] = new AA();
arr[i][2] = new AA();
}
8.2 容器概论
1 java容器有两个分支:
——分支一:Collection分支,其下分为List和Set, List分为链表(LinkedList),数组(ArrayList),向量(Vector,线程安全),Vector下面是Stack。Set有HashSet
![【黑马程序员】八、Java基础:数组和容器 - ql7450820 - 鬼哥的博客](http://img1.ph.126.net/BMthMF4f1R5BBV08J60zgA==/6597708582913584532.png)
![【黑马程序员】八、Java基础:数组和容器 - ql7450820 - 鬼哥的博客](http://img1.ph.126.net/x0r01sX3g9onrOMtUdgAjA==/6598001053006590823.png)
2 容器的选择:
什么时候使用什么容器?
使用数组时,考虑ArrayList,如果要求线程安全,则使用Vector,这两个:
——随机访问速度快(下标get(i))
——但频繁的插入和删除效率低,因为底层使用数组实现,需要重新分配空间,大量复制
——每个元素不需要额外的前续和后续指针,所以空间利用率高
使用链表时,考虑LinkedList:
——随机访问速度慢,所以对链表一般不使用get(i)来访问单个元素
——插入和删除效率高:对于容器的大小改变反应迅速
使用关联数组时,考虑HashMap,如果要求线程安全,使用HashTable:
——根据键直接找到值的存放地址
——散列函数
8.3 List之ArrayList
容器最大的一个好处就是大小是可变的,数组的大小不可变,一旦确定,就不能再更改。
1 创建:
List<T> arr = new ArrayList<T>();
2 添加元素:
——允许添加null:arr.add(null)
——arr.add(T t);
——arr.add(int pos, T t);
// 在指定下标处插入,即之前下标为pos及其之后的元素都依次后移
// arr.add(arr.size()-1, t)即在尾部插入,下表再大,就是出界异常了
——addAll(collection<?> c)
——addAll(int pos, collection<?> c)
//添加一个元素序列,或在指定下标处开始添加
3 访问元素:
——既然可以add null,则get的可能是null,使用时需要注意
T t = arr.get(i)
4 删除元素:
——remove(int pos); //删除下标为pos的元素
——remove(T t);
//删除值为t的元素,用t.equals()判断是否相等
// 注意equals的定义是:public boolean equals(Object b){}
//如果有重复,只删除第一个
——remove(collection<?> c);
//删除容器c中出现的所有元素,
//对于任意一个c中的元素,只删除其第一次出现
//相当于:foreach(t in c){ remove(t); }
//arr.remove(arr):相当于删除arr中所有元素
——clear(): 清空所有元素
5 查找:
——查找需要进行的判断是否相等的操作,全都有T.equals(Object o)代劳
——arr.contain(T t)
//arr中含有元素与t相等的元素,则返回true,否则,返回false
//使用T.equals(Object o)进行判断是否相等
——arr.indexOf(T t)
//arr中含有值与t相等的元素,返回其下标,无则返回-1
——arr.lastIndexOf(T t)
//倒着看,即最后一个与t相等的元素的下标
6 大小:
——arr.size(): //返回元素个数
——arr.isEmpty(); //容器为空,即size() = 0,则返回true
7 转化为数组:
——object[] arrs = arr.toArray();
——T[] arrs = arr.toArray(new T[length]);
// if参数中的length>arr.size(), 则arrs元素个数为length,多的为null
//if参数中的length<=arr.size(), 则arr中元素全部转过来
//arr中所有元素永远都会全转到数组里
——List<T> arr2 = arr.subList(int from, int to)
//从arr的下标from开始,截取到下标为to-1,放到arr2中
//注意别出界,to最大可以为size(),因为不取arr.get(to),
//最多取到arr.get(to-1)
//如果to<from,则抛出异常IllegalArgumentException,非法参数
8 ArrayList的迭代器:
——对于迭代器,首先要知道的是迭代器是一种特殊的类型,其内部总是有一个游标,始终指向当前正访问的元素:一切操作都是围绕他进行的。
(1)Iterator
——基本用法:
Iterator<T> it = arr.iterator();
while(it.hasNext()){
T t = it.next();
// operate t …
}
问题:
Iterator内部应该有个游标,始终指向当前正在访问的元素,并且只随着next()而变化,那Iterator是否只能被遍历一次?
——YES!Iterator只能被遍历一次,从头走到尾,不管以分几次走的,走到头就回不来了!
(2)ListIterator
获取迭代器:ListIterator<T> lit = arr.listIterator();
向后遍历:while(lit.hasNext()) { T t = lit.next(); }
向前遍历:while(lit.hasPrevious()) { T t = lit.previous(); }
添加元素:lit.add(Object o); //T t
删除元素:lit.remove(); //对应于向前或向后模式,删除当前元素
返回当前元素的下标:
——nextIndex(); //即下一次next()将要返回的元素的下标
——previousIndex(); //即下一次previous()将要返回的元素的下标
——在头或在尾了,就返回-1
替换:
——lit.set(Object o)
——将当前元素替换为参数指定的对象
8.4 List之 Vector
1 创建:
——Vector<T> vec = new Vector<T>();
——T应该实现了equals,toString等方法
2 添加元素:
——add系列1:
追加,每一次add都改变了大小(效率低),请参考ArrayList的add系列。
——add系列2:
addElement(T t);
——set系列1:先定好大小,再一个一个set,只改变一次大小,效率高。
vec.setSize(10); //现在vec有10个元素了,都是null
vec.set(0, t);
…
vec.set(9, t);
——set系列2:
vec.setSize(10);
vec.setElementAt(T t, int index); //你的存在有意义吗?
3 大小:
——vector的内存分配操作比较丰富
——概念1:分配了的,但是还没用的,连初始化都没有的,是capacity
——概念2:已经用了的,size
(1)size系列:
——vec.setSize(new_size); //设置了new_size个null元素
——此时:vec.size() = 10
——setSize()总是会重新分配指定个数的空间,会产生几种情况:
情况1:new_size大于现有空间,则将原来的拷贝过来,其余为null
情况2:new_size小于现有空间:截取了,之前的元素没有全部过来
情况3:等于,没事
(2)capacity系列:
——vec.ensureCapacity(100);
——此时:vec.capacity() = 100
——同时:vec.size()并不受这个操作的影响
——注意capacity分配的空间可以用,不需要重新分配,但是此时还没初始化。
(3)vec.isEmpty()
——对应于size() = 0
(4)vec.trimToSize()
——这个么意思?释放了未使用的空间,导致capacity() = size()
4 访问元素:
——T t = vec.get(index)
——T t = vec.elementAt(index)
——T t = vec.firstElement()
——T t = vec.lastElement()
5 清除:
——clear():清空
——removeAllElement(): 清空
——remove(index)
对应于:removeElementAt(index)
——remove(T t)
对应于:removeElement(T t)
——remove(collection<?> c)
移除vector中出现的c中的元素,只一次,参考ArrayList
6 查找:
——vec.contains(Object o)
——vec.containsAll(collection<?> c)
——indexOf(Object o):无则返回-1
——indexOf(Object o, int index)
index指定了查找的起点,如果index为0,即从头开始查找
7 迭代器:
——Iterator和ListIterator,参考ArrayList的迭代器
8 转换为数组:
——参考ArrayList
9 截取子序列:
——参考ArrayList
8.5 List之LinkedList
1 创建:
LinkedList<T> list = new LinkedList<T>();
2 插入:
——add系列参照ArrayList
——链表特有的add:
list.addFirst(T t);
list.addLast(T t);
3 访问元素:
——T t = list.get(index)
——T t = list.getFirst();
——T t = list.getLast();
4 大小:
——size()
——isEmpty()
5 转换成数组,截取子序列
——参照ArrayList
——toArray()
——toArray(T[])
——subList(from, to)
6 查找:
——indexOf()
——lastIndexOf()
——contains()
——containsALl()
——参照ArrayList
7 替换:
——list.set(int pos, T t):使用t替换下标为pos的元素
8 删除:
——remove()
——remove(index)
——remove(T t)
——removeAll(collection<?> c)
——removeFirst()
——removeLast()
——retainAll(collection c):删除list中所有不在c中出现的元素
9 迭代器:
——Iterator,ListIterator
——参考ArrayList
10 链表特有的操作:
——list.offer(T t):offer就是给,给list一个元素,追加到尾部
——peek(): 返回首元素,即get(0),这里不会remove首元素
——poll():返回首元素,并且将其remove掉
8.6 HastSet和TreeSet
HashSet是个有意思的东西,什么时候能用这个?
——HashSet:散列表,值不能重复,允许有null,但只能有一个
——HashSet没有get方法,但是有迭代器,其元素还是可以访问的,但你使用HashSet不是为了访问方便,而是为了查找
——HashSet注重查找时间,内部不会排序保存,按插入顺序保存,其保存的元素T需要实现hashCode(),equals()
——TreeSet是底层为tree的一种有序的set,内部数据进行有序排列,这个顺序是你能指定的吗?需要你实现comparable接口来指定排序方式。
1 创建:
Set<T> set = new HashSet<T>();
2 添加元素:
set.add(T t);
set.addAll(collection<?> c)
——需要你保证的是添加的元素不能重复,包括c中的元素,不能有重复
——可以添加null, 但只能一次
3 查找:
——set.contains(T t)
——set.containsAll(collection<?> c)
4 删除元素:
——clear()
——remove(Object o)
——removeAll(collection c):删除set中出现的c中的元素
——retainAll(collection c):保留set中出现的,不在c中的元素,其余删除
5 大小:
——size()
——isEmpty()
6 转换成数组
——toArray()
——toArray(T[])
——参照ArrayList
7 迭代器:
Iterator,好像没ListIterator。
8.7 HashMap
——非线程安全的Map,如果想要线程安全你的,就考虑HashTable
——但是二者的接口都一样
1 创建:
Map<String, T> map = new HashMap<String, T>()
——Map<Key, Value>
2 添加键值对:
——map.put(String, T)
——map.put(Map map2)
3 访问:按key值访问
——T t = map.get(Key key); //实际上接收的是object参数
4 删除:按key值删除
——clear():清空
——remove(Key key); //实际上接收的是Object参数
5 大小:
——size()
——isEmpty()
6 查找:
(1)看是否包含Key值:map.containKey(Key key);
(2)看是否包含Value值:map.containValue(Value value);
——都是使用equals()进行比较
7 map中所有值组成的collection:
collection<Value> c = map.values();
ArrayList<Value> arr = new ArrayList<Value>( c );
8 map中所有key组成的set:
Set<Key> s = map.keySet();
Set<Key> set = new HashSet<Key>( s );
9 map中的键值对组成的entry的set:
8.8 HashTable
——接口和HashMap是一样的,参考HashMap
8.9 WeakHashMap
——这个是干什么的?
——如果外部没有引用指向WeakHashMap中的元素,则此元素就会被删除并回收?
8.10 java数组和容器工具包:java自带及再封装
注意:java的数组可以直接存放基本数据类型,但是容器却不能,java并没有提供直接存储基本数据类型的容器,只能将它们放入对应的包装类中再放入容器。
——java的容器只处理对象
1、数组
——数组在java中要当做对象来看待,只不过是个特殊对象,其类型是int[]。
——存储和访问对象引用序列的效率最高
——但是数组大小是固定的,其生命周期也不可改变
——如果数组空间不足,你需要手动创建一个新数组,把之前的数组的元素移过来。
——对于基本数据类型来说,元素值就是值,不是引用
class Weeble {}
(1)数组初始化:
——Weeble[] arr1 = new Weeble[5]; //之后数组大小就固定了,就5个null元素
——Weeble[] arr = {new Weeble(), new Weeble()};//数组固定2个元素
——Weeble[] arr = new Weeble[]{new Weeble(), new Weeble()}; //还是2个元素
(2)Arrays类:
——对应于java数组的类,提供一些操作数组的静态方法
——自定义工具库中有一些扩展增强的java数组操作方法。
——这些方法都是static的,都有对于各种基本类型和Object的重载
equals():比较两个数组是否相等——
fill():使用指定值填充整个数组
sort():对数组排序
binarSearch():从排好序的数组中查找元素
asList():将数组转化为List
toString():将指定数组作为字符串输出,省的每次都用for打印
数组的toString():方便打印
——SVArray.toString(int[])
——对boolean ,char, byte, short, int, long, float, double, Object做了重载
数组的填充fill(): 方便测试
——SVArray.fill(int[], Generator gen):使用gen生成器来填充数组
——SVArray.fill(int[], int from, int to, gen):to作为下标,此元素不会被填充
——现在只针对各种基本类型和String定义了RandXXXGenerator,提供nextXXX方法生成一个随机的值
数组的复制:
——首先要知道:java自带的System.arraycopy()方法能够复制数组,且速度比for要快!
——SVArray封装了此方法
——对于对象来说,此方法只能浅拷贝,即两个数组虽然不是指向同一个内存,但其中的各个元素指向的其实是同一个对象!
——SVArray.arrayCopy(int[] from, int start1, int[] to, int start2, int length )
——将from数组的从start1开始的length个元素复制给数组to的start2开始length个元素
——任何越界都会抛出越界异常
——有对boolean, byte,char ,short, int, long, float, double, Object的重载
数组的比较:Array.equals(arr1, arr2)
——SVArray对此进行了封装SVArray.equals()
——对基本类型和Object做了重载
——比较的是值,而不是引用,首先要求数组元素个数相同,然后使用Object或者对应包装类的equals()方法进行比较,都相等,则数组相等
数组的排序:
——Arrays.sort(数组)
——得到升序排列的数组
——对基本类型和接口Comparable进行了重载
——对于Object,需要是实现了接口Comparable的类,因为这个类提供了一个compareTo()的方法,实现了这个方法的类才能进行比较,从而用于sort
——因为这里java无法提供大于和小于运算符的重载
到现在为止,你如果要创建一个类,此类要放在数组中,而且还可能会被排序,应该这样:
class WillBeSorted implements Comparable{
public int x = 0;
public int compareTo(Object ws){
WillBeSorted wss = (WillBeSorted)ws;
if(x < wss.x) return -1;
else if(x == wss.x) return 0;
else return 1;
}
public WillBeSorted(int xx){
x = xx;
}
public static Random rand = new Random();
public static Generator generator(){
return new Generator(){
public WillBeSorted next(){
return new WillBeSorted(rand.nextInt(100));
}
};
}
public String toString(){
return x+"";
}
}
——用法是:
WillBeSorted[] wss = new WillBeSorted[4];
SVArray.fill(wss, WillBeSorted.generator());
System.out.println(SVArray.toString(wss));
Arrays.sort(wss);
System.out.println(SVArray.toString(wss));
对于String来说,排序就是字典序,大小写区分,如果要定义大小写不区分的String排序,需要用到接口:Comparator
class StringCompareNoCase implements Comparator
{
public int compare(Object obj1, Object obj2)
{
String s1 = ((String)obj1).toLowerCase();
String s2 = ((String)obj2).toLowerCase();
return s1.compareTo(s2);
}
}
——用法如下:
String[] ss = new String[10];
SVArray.fill(ss, new SVArray.RandStringGenerator(6));
System.out.println(SVArray.toString(ss));
Arrays.sort(ss, new StringCompareNoCase());
System.out.println(SVArray.toString(ss));
关于java的排序和比较的总结:
——java对数组排序使用:Arrays.sort()
——传入要比较的数组,如果只有一个参数,则此数组的元素类型必须实现接口Comparable的compareTo方法
——如果需要传入第二个参数来改变sort的比较方式,即改变数组元素类型本来的compareTo()的行为,就需要另创建一个类,此类需要实现接口Comparator的compare()方法
——涉及到排序的比较就是这样
数组的查找:
——Arrays.binarySearch()
——只能对已排序的数组进行查找
——返回的是下标
——排序所使用的Comparator和查找所使用的Comparator必须是同一个!!