黑马程序员 数组和容器

 
------- android培训 java培训 、期待与您交流!-------

数组:可以存放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 - 鬼哥的博客
——分支二:Map分支,其下分为HashTable(线程安全),HashMap,WeakHashMap
【黑马程序员】八、Java基础:数组和容器 - ql7450820 - 鬼哥的博客

  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必须是同一个!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值