黑马程序员: 集合框架
2013-03-23 16:30
204 查看
-------
android培训、java培训、期待与您交流! ----------
集合框架
为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式.
数组和集合类同时容器,有何不同?
数组虽然也可以存储对象,但长度固定;集合长度可变,数组可以储存基本数据类型,集合只能存储对象,
集合类的特点: 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象.
各种集合不断向上抽取,形成集合框架.
![](http://img.my.csdn.net/uploads/201303/23/1364027624_6153.jpg)
每个容器的存储方式都有不同,这个存储方式叫数据结构
Collection 接口
添加:add(Objecto) 删除:remove() ,clear() 判断:contains() 获取: size()
Contains(Object o) 判断是否包含有某元素, remove()
Retain(Collection c)//取交集, 只保留此集合和集合c中相同的元素
iterator() 返回一个Iterator对象
迭代器
Iterator 接口
只有三个方法: HasNext()判断迭代是否还有元素 Next()返回迭代下一个元素
remove()从迭代器指向的 collection 中移除迭代器返回的最后一个元素.
迭代器就是遍历(迭代访问)集合中的元素.
对于遍历容器这个操作不足以用一个函数来描述,即迭代取出操作不止一个动作.他需要用多个动作来体现,一般将这多个动作封装到一个对象中去,所以各个容器中都有一个取出对象,因为数据结构不同,每一个容器取出元素的方式细节都不一样. 这取出就需要一个类来(统一)完成,而这个类就定义在了各个集合的内部(因为要操作集合内部的元素,可直接访问集合内容的元素).而不管容器如何,都是先判断再取出,可将这些共性抽取 形成接口àIterator.
统一了集合的迭代方式, 即使以后再新加集合容器也不怕,也是用其内部提供的Iterator来取
Iterator相当于操纵杆,我们不管各个游戏机内部怎样,只要他们的操纵杆一样就好了
建议使用for循环,理由如下,小细节.
Iterator it = c.iterator();
While(it.hasNext) for(Iterator it = c.iterator;it.hasNext)
Println(it.next()); //循环结束,it还存在 println(it.next());//循环结束,it消失
List
特有方法: 凡是可以操作角标的方法都是该体系特有方法
增: add(index,element) 删:remove(index)改:set(index,element) 查:get(index) subList(from,to) listIterator();
获取: size() 遍历: 角标遍历 iterator() listIterator() (列表迭代器);
在迭代时,不可以通过集合对象的方法操作对象中的元素,因为会发生ConcurrentModificationException异常.
所以 在迭代器时,只能用迭代器的方法操作元素(判断 取出 删除)
在用Iterator遍历list时,不能并发修改容器里的元素(例如:我在取,你在添加,我取不取呢),只可以用Iterator的remove()方法,因为remove移除的是迭代器返回的最后一个元素.
而List特有的ListIterator则不仅能删除,还能添加和修改.(增删改查 逆向遍历)
Collecion
|--List;元素有序,可以重复,有索引
|--Arraylist: 底层的数据结构使用的是数组结构.特点:查询速度很快.但删增很慢
|--LinkedList: 底层的数据结构是链表数据结构 特点:增删速度很快,但查询稍慢
|--Vector: 底层是数组数据结构 和arrayList一样,被其替代,同步的,而arrayList不同步.
1.2时候出现集合框架,之前都是用vector(1.0就出现了)
可变长度: 当内部数组快满了的时候,自动创建一个更大的数组 ,将原来数组中的元素复制过去
|--Set:元素无序,不可重复,
|--HashSet: 底层结构àHash表 不同步
|--TreeSet:: 底层结构à二叉树 可以对Set集合中的元素进行排序.
Map
|--HashTable 底层数据架构à哈希表,不可以存入键为null键或null值,线程同步效率低jdk1.0
|--HashMap: 底层数据结构à哈希表, 允许使用null值或null键,线程不同步效率高/jdk1.2
|--TreeMap 底层数据结构à二叉树, 线程不同步.可以用于给map集合中的键进行排序
和Set很像,
其实set底层就是使用了Map集合,Set是特殊的Map.(Set相当于值都为某个固定的对象的Map键值对(,键为Set中元素,值固定)).
Map的键不能重复,所以Set中的元素也不能重复
可以看出,老的集合都是线程同步的效率低,被替代了,新的接口都是不同步的,
Vector
枚举(Enumeration)接口很像迭代器(iterator) 是vector特有的遍历方式. Vector.elements()可得到Enumeration.
其实枚举和迭代器是一样的,因为枚举的名称以及方法的名称过长,就被迭代器取代了,枚举郁郁而终了.
LinkList
特有方法: AddFirst() addLast()
Getfirst() getLast()获取元素,但不删除元素.如果没有元素.会出现NoSuchElementException.
Removefirst() removeLast()获取元素,并删除元素.如果没有元素.会出现NoSuchElementException.
Jdk1.6出现了新方法:
offerFirst() offerLast(); peekFirst() peekLast();
pollFirst() pollLast()获取元素,并删除元素.如果没有元素,返回空
用LinkList模拟堆栈或队列数据结构 (例子见文档末尾)
堆栈:先进后出如同杯子
队列:先进先出, First In First Out FIFO如同一个水管.
//去除ArrayList集合中重复的元素(用contains()方法判断每个元素,若为false才加入一个临时容器)
将自定义的对象作为元素存到ArrayList集合中,并去除重复元素
自定义对象中中重写equals()方法, contain()和remove()都依赖equals.
contains( Ojbect o) ( 此方法拿o和容器中的元素e一一比较)) (o==Null?e==Null:o.equals(e)).
List集合判断元素是否相同,依据的是元素的equals()方法.
Set: Set集合的功能和Collection一致.
hashSet:
存元素时.先判断hashcode是否相同, 若不同,直接存.若相同再用equals()判断是否相同. (保证元素的唯一性)
所以要判断元素是否存在.和删除等操作,依赖者2个方法,所以对象要重写hashCode方法和equals()方法.
例: public hashCode(){returnname.hashCode()+age*37} // 两参数相加很可能相等.”*37”是为了尽量使hashCode不相等.
hashset是基于hashMap实现的(内部封装一个HashMap成员变量),hashSet底层采用HashMap来保存所有元素,其方法也是调用HashMap的方法.
(6) hashCode方法必须与equals方法必须兼容
如果我们自己定义了一个类,想对这个类的大量对象组织成散列表结构便于查找。有一点一定要注意:就是hashCode方法必须与equals方法向兼容。
Java代码:
//hashCode与equals方法的兼容
public class Employee{
public int id;
public Stringname="";
//相同id对象具有相同散列码
public int hashCode(){
return id;
}
//equals必须比较id
public booleanequals(Employee x){
if(this.id==x.id)return true;
else returnfalse;
}
}
为什么要这样,因为HashSet不允许相同元素(equals==ture)同时存在在结构中。假如employeeX(1111,“张三”)和employee(1111,"李四"),而Employee.equals比较的是name。这样的话,employeeX和employeeY的equals不相等。它们会根据相同的散列码1111加入到同一个散列单元所指向的列表中。这种情况多了,链表的数据将很庞大,散列冲突将非常严重,查找效率会大幅度的降低。
如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。如上段所述,如果hashcode相等,但是值不同,情况多了,那个hashcode
对应的散列单元必将有很多数据,查找的效率会大幅度降低.
HashSet不能重复存储equals相同的数据 。原因就是equals相同,数据的散列码也就相同(hashCode必须和equals兼容)。大量相同的数据将存放在同一个散列单元所指向的链表中,造成严重的散列冲突,对查找效率是灾难性的。
treeSet
加入treeSet的元素必须实现Comparable接口或让TreeSet具备比较性, 保证元素唯一性的依据:compareTo()方法.
TreeSet排序的第一种方式: 让元素自身具备比较性(实现Comparable)à自然顺序
TreeSet排序的第二种方式: 当元素不具备比较性,或者具备的比较性不是需要的,让TreeSet自身具备比较性.怎么让TreeSet自身具备比较性: 定义比较器(Comparator),将比较器对象作为参数传递给TreeSet构造函数.
当两种排序方式都存在时,以比较器为主.
比较器比较好,常用 灵活,可扩展
Comparable //该接口强制让实现了它的子类具备比较性.只有一个方法compareTo(To); (例子见文档后)
Comparator: compare(T o1, T o2); equals(Object obj); (例子见文档后)
练习:按照字符串长度排序
à字符串本身具备比较性,但是它的比较方式不是所需要的,只能使用比较器, (例子见文档后)
泛型
在使用java提供的对象时,什么时候使用泛型呢
通常在集合框架中很常见,只要见到< > 就要定义泛型.
其实< > 就是用来接收类型的.
当使用集合时,将集合中要存储的数据类型作为参数传递到 < >中即可.
当类中要操作的引用数据类型不确定的时候,再起定义Object来完成扩展,现在定义泛型来完成扩展.
//泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就应经固定了
为了让不同方法可以操作不同的类型,而且类型还不确定,
那么可以将泛型定义在方法上 他们可同时存在
静态方法不可以访问类上定义的类型,如果静态要操作的数据类型不确定,可以将泛型定义在方法上.
泛型还可以定义在接口上 写法: class InterImpl <T> implements Inter<T>
通配符 ArrayList<?> 表示类型不明确,用?也可理解为占位符,
泛型不能使用类型特有方法,因为类型确定.
泛型的限定: ?extends E: 可以加收E类型或者E的子类型.上限.
?super E: 可以接收E类型或者E的父类型,下限.
TreeSet<E> 代表一个有序的元素为E的树,它其中的一个构造器需要一个Comparator类来比较两个元素,以E为String类时为例,此时的Comparator可以是Comparator<String>,也可以是Comparator<Object>,但Comparator<Integer>就不行,如何表示这样的限制呢?
jdk源代码中是这样的:
public TreeSet(Comparator<? super E> c) //接口Comparator的<
>里面可以为E类型和E的父类,因为父类引用可以接收子类对象.这样就保证了传给构造器的Comparator是可以进行E元素的比较的。
Map (K,V)
该集合存储键值对,一对一对的往里存,而且要保准键的唯一性.
该接口的主要共性方法
1. 添加 put(K key, V value) putAll(Map<? extends K, ? extends V > m)
2. 删除 clear() remove(Object key)
3. 判断 containsValue(Object value) containsKey(Object key) isEmpty()
4. 获取 get(Object key) size() values() entrySet() keyset();
5.
values() 返回此映射中包含的值的
和clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
put() 添加时,相同的键,那么后添加的值会覆盖原有键的对应值,并put方法返回被覆盖的值
keyset() 返回此映射中包含的映射关系的Set视图
将map中的所有键存入Set集合,因为Set具备迭代器.
所有可以迭代方式取出所有的键,再根据get方法,获取每一个键的对应的值.
entrySet() 返回此映射中包含的映射关系的Set视图 返回的数据类型Set(Map.Entry(K,V))
Map.entry也是一个接口,他是Map接口中的内部接口.
先有Map集合,再有Entry(键值关系),且Entry需要直接访问Map中的元素,所以Entry接口封闭在Map接口内部
加static 的接口一定是内部接口,因为只有在成员位置上才能加static 静态修饰符.
练习:”dsfefdsfdfegg”获取该字符串中的字母出现的次数. (例子见文档尾).
map集合被使用是应为具备映射关系
Collections
排序 sort
取最大值 :max max(Collection)
binarySearch()二分查找,(调用前先调用sort()方法对集合排序).
如果搜索键包含在列表中,则返回搜索键的索引(可自定义比较器).没找到返回 (-(插入点) - 1).
fill() 将list集合中所有元素替换成指定元素 replacAll()替换 reverse()反转元素, synchronizedList()同步化List.
swap() 交换元素位置, shuffle() 洗牌,打乱顺序
reverseOrder() 排序反转
例子:TreeSet(Collections.reverseOrder())//将集合的自然顺序逆转. TreeSet(Collection.reverseOrder ( Comparator c))//将比较器逆转,在传给TreeSet
Collection.toArray(new String
) 集合转数组
当指定类型的数组长度小于集合的size,那么该方法内部创建一个新数组,长度为集合的size
当指定类型那个的长度大于集合的size,就不会创建了数组,而是使用传进来的数组
为什么集合变数组,限定对元素的操作.只能读,不能删改增.
Array
binarySearch() deepEquals(),还比较数组里的元素(如容器)里元素.可用于多维数组判断
fill()还可指定范围填充数组 sort()还可以局部排序
asList()把数组变成集合,集合可用方法多.(如果数组元素是基本类型,那么会将该数组作为集合中的元素存在.)
增强for循环:
只能获取集合元素,不能对集合进行操作,iterator除了遍历,还可以进行remove操作
如果用ListIterator,还可以在遍历过程中对集合进行增删改查的操作.
传统for和高级for的区别
高级for的一个局限性,必须有遍历的目标,比如循环打印输出一句话,for就不行.
传统for可以定义角标,可操作(判断)角标
//用stack模拟堆栈, (模拟队列类似)
class Stack{
private
LinkedList link;
Stack(){
link = newLinkedList();
}
public
void add(Object o){
link.addFirst(o);
}
public Object get(){
returnlink.removeLast();
}
public
boolean isNull(){
returnlink.isEmpty();
}
}
================================================================
//Comparable例子
class Studentimplements
Comparable {
public Stringname;
public
int age;
public
int compareTo(Object obj) {
if (!(objinstanceof Student))
throw
new RuntimeException("不是学生对象");
Student s = (Student) obj;
if (this.age > s.age)
return 1;
if (this.age == s.age) //如果光比较age,当向TreeSet添加Student元素时,年龄相同的添加不进去
return this.name.compareTo(s.name); //String已经实现了Comparable
return -1;
}
}
//Comparator 例子
class MyComparatorimplements
Comparator{
public
int compare(Object o1,Object o2){
Student s1 = (Student) o1;
Student s2 = (Student) o2;
int num = s1.name.compareTo(s2.name);
if(num==0)
num = s1.age-s2.age;
return num;
}
}
//以字符长度排序Comparator
class StrLenComparatorimplements Comparator<String >{
public
int compare(String o1,String o2){
int num = newInteger(o1.length()).compareTo(new Integer(o2.length()));
if(num==0)
return o1.compareTo(o2);
return num;
}
}
//” dsfefdsfdfegg”获取该字符串中的字母出现的次数.
// 1, 将字符串转换成字符数组,因为要对每一个字母进行操作
// 2, 定义一个map集合,因为打印结果的字母有顺序,所以使用treeMap集合
// 3,遍历祖父数组,将每一个字母作为见去查map集合,如果返回null,将该字母和1存入到map集合中.
//如果返回不是null,说明该字母在map集合已经存在并有对应次数,那么就获取盖子出并进行自增,然后再存入map
public
static void charCount(Stringstr){
char[] chs = str.toCharArray();
TreeMap<Character,Integer> tm = newTreeMap<Character,Integer>();
int count = 0;
for(int x = 0; x<chs.length;x++){
Integer value = tm.get(chs[x]);
if(value!=null)
count = value;
count++;
tm.put(chs[x], count);
count = 0;
/*if(value==null){
tm.put(chs[x], 1);
}
else{
value = value + 1;
tm.put(chs[x], value);
}*/
}
System.out.println(tm);
}
//二分查找
public
static int halfSearch(List<String> list,String key){
int max, min,mid;
max = list.size()-1;
min=0;
while(min<=max){
mid = (max+min)>>1;// /2;
String str = list.get(mid);
int num = str.compareTo(key);
if(num>0)//如果mid大于key,那么max=mid-1;
max = mid-1;
else
if(num<0)//如果mid小于key,那么min=mid+1;
min =mid +1;
else
//如果mid==key,找到了返回值
return mid;
}
return min;//如果没找到,返回插入点;
}
android培训、java培训、期待与您交流! ----------
集合框架
为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式.
数组和集合类同时容器,有何不同?
数组虽然也可以存储对象,但长度固定;集合长度可变,数组可以储存基本数据类型,集合只能存储对象,
集合类的特点: 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象.
各种集合不断向上抽取,形成集合框架.
![](http://img.my.csdn.net/uploads/201303/23/1364027624_6153.jpg)
每个容器的存储方式都有不同,这个存储方式叫数据结构
Collection 接口
添加:add(Objecto) 删除:remove() ,clear() 判断:contains() 获取: size()
Contains(Object o) 判断是否包含有某元素, remove()
Retain(Collection c)//取交集, 只保留此集合和集合c中相同的元素
iterator() 返回一个Iterator对象
迭代器
Iterator 接口
只有三个方法: HasNext()判断迭代是否还有元素 Next()返回迭代下一个元素
remove()从迭代器指向的 collection 中移除迭代器返回的最后一个元素.
迭代器就是遍历(迭代访问)集合中的元素.
对于遍历容器这个操作不足以用一个函数来描述,即迭代取出操作不止一个动作.他需要用多个动作来体现,一般将这多个动作封装到一个对象中去,所以各个容器中都有一个取出对象,因为数据结构不同,每一个容器取出元素的方式细节都不一样. 这取出就需要一个类来(统一)完成,而这个类就定义在了各个集合的内部(因为要操作集合内部的元素,可直接访问集合内容的元素).而不管容器如何,都是先判断再取出,可将这些共性抽取 形成接口àIterator.
遍历:对于集合数据而言,访问所有的数据即为遍历。遍历的方法可以用递归或者迭代。 迭代:一般是用同一个参数来表示每个集合元素,用循环来实现。 递归:是利用计算机的堆栈的概念,一般通过调用相同的函数来实现,函数中一般会设置终止的语句
统一了集合的迭代方式, 即使以后再新加集合容器也不怕,也是用其内部提供的Iterator来取
Iterator相当于操纵杆,我们不管各个游戏机内部怎样,只要他们的操纵杆一样就好了
建议使用for循环,理由如下,小细节.
Iterator it = c.iterator();
While(it.hasNext) for(Iterator it = c.iterator;it.hasNext)
Println(it.next()); //循环结束,it还存在 println(it.next());//循环结束,it消失
List
特有方法: 凡是可以操作角标的方法都是该体系特有方法
增: add(index,element) 删:remove(index)改:set(index,element) 查:get(index) subList(from,to) listIterator();
获取: size() 遍历: 角标遍历 iterator() listIterator() (列表迭代器);
在迭代时,不可以通过集合对象的方法操作对象中的元素,因为会发生ConcurrentModificationException异常.
所以 在迭代器时,只能用迭代器的方法操作元素(判断 取出 删除)
在用Iterator遍历list时,不能并发修改容器里的元素(例如:我在取,你在添加,我取不取呢),只可以用Iterator的remove()方法,因为remove移除的是迭代器返回的最后一个元素.
而List特有的ListIterator则不仅能删除,还能添加和修改.(增删改查 逆向遍历)
Collecion
|--List;元素有序,可以重复,有索引
|--Arraylist: 底层的数据结构使用的是数组结构.特点:查询速度很快.但删增很慢
|--LinkedList: 底层的数据结构是链表数据结构 特点:增删速度很快,但查询稍慢
|--Vector: 底层是数组数据结构 和arrayList一样,被其替代,同步的,而arrayList不同步.
1.2时候出现集合框架,之前都是用vector(1.0就出现了)
可变长度: 当内部数组快满了的时候,自动创建一个更大的数组 ,将原来数组中的元素复制过去
|--Set:元素无序,不可重复,
|--HashSet: 底层结构àHash表 不同步
|--TreeSet:: 底层结构à二叉树 可以对Set集合中的元素进行排序.
Map
|--HashTable 底层数据架构à哈希表,不可以存入键为null键或null值,线程同步效率低jdk1.0
|--HashMap: 底层数据结构à哈希表, 允许使用null值或null键,线程不同步效率高/jdk1.2
|--TreeMap 底层数据结构à二叉树, 线程不同步.可以用于给map集合中的键进行排序
和Set很像,
其实set底层就是使用了Map集合,Set是特殊的Map.(Set相当于值都为某个固定的对象的Map键值对(,键为Set中元素,值固定)).
Map的键不能重复,所以Set中的元素也不能重复
可以看出,老的集合都是线程同步的效率低,被替代了,新的接口都是不同步的,
Vector
枚举(Enumeration)接口很像迭代器(iterator) 是vector特有的遍历方式. Vector.elements()可得到Enumeration.
其实枚举和迭代器是一样的,因为枚举的名称以及方法的名称过长,就被迭代器取代了,枚举郁郁而终了.
LinkList
特有方法: AddFirst() addLast()
Getfirst() getLast()获取元素,但不删除元素.如果没有元素.会出现NoSuchElementException.
Removefirst() removeLast()获取元素,并删除元素.如果没有元素.会出现NoSuchElementException.
Jdk1.6出现了新方法:
offerFirst() offerLast(); peekFirst() peekLast();
pollFirst() pollLast()获取元素,并删除元素.如果没有元素,返回空
用LinkList模拟堆栈或队列数据结构 (例子见文档末尾)
堆栈:先进后出如同杯子
队列:先进先出, First In First Out FIFO如同一个水管.
//去除ArrayList集合中重复的元素(用contains()方法判断每个元素,若为false才加入一个临时容器)
将自定义的对象作为元素存到ArrayList集合中,并去除重复元素
自定义对象中中重写equals()方法, contain()和remove()都依赖equals.
contains( Ojbect o) ( 此方法拿o和容器中的元素e一一比较)) (o==Null?e==Null:o.equals(e)).
List集合判断元素是否相同,依据的是元素的equals()方法.
Set: Set集合的功能和Collection一致.
hashSet:
存元素时.先判断hashcode是否相同, 若不同,直接存.若相同再用equals()判断是否相同. (保证元素的唯一性)
所以要判断元素是否存在.和删除等操作,依赖者2个方法,所以对象要重写hashCode方法和equals()方法.
例: public hashCode(){returnname.hashCode()+age*37} // 两参数相加很可能相等.”*37”是为了尽量使hashCode不相等.
hashset是基于hashMap实现的(内部封装一个HashMap成员变量),hashSet底层采用HashMap来保存所有元素,其方法也是调用HashMap的方法.
(6) hashCode方法必须与equals方法必须兼容
如果我们自己定义了一个类,想对这个类的大量对象组织成散列表结构便于查找。有一点一定要注意:就是hashCode方法必须与equals方法向兼容。
Java代码:
//hashCode与equals方法的兼容
public class Employee{
public int id;
public Stringname="";
//相同id对象具有相同散列码
public int hashCode(){
return id;
}
//equals必须比较id
public booleanequals(Employee x){
if(this.id==x.id)return true;
else returnfalse;
}
}
为什么要这样,因为HashSet不允许相同元素(equals==ture)同时存在在结构中。假如employeeX(1111,“张三”)和employee(1111,"李四"),而Employee.equals比较的是name。这样的话,employeeX和employeeY的equals不相等。它们会根据相同的散列码1111加入到同一个散列单元所指向的列表中。这种情况多了,链表的数据将很庞大,散列冲突将非常严重,查找效率会大幅度的降低。
如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。如上段所述,如果hashcode相等,但是值不同,情况多了,那个hashcode
对应的散列单元必将有很多数据,查找的效率会大幅度降低.
HashSet不能重复存储equals相同的数据 。原因就是equals相同,数据的散列码也就相同(hashCode必须和equals兼容)。大量相同的数据将存放在同一个散列单元所指向的链表中,造成严重的散列冲突,对查找效率是灾难性的。
treeSet
加入treeSet的元素必须实现Comparable接口或让TreeSet具备比较性, 保证元素唯一性的依据:compareTo()方法.
TreeSet排序的第一种方式: 让元素自身具备比较性(实现Comparable)à自然顺序
TreeSet排序的第二种方式: 当元素不具备比较性,或者具备的比较性不是需要的,让TreeSet自身具备比较性.怎么让TreeSet自身具备比较性: 定义比较器(Comparator),将比较器对象作为参数传递给TreeSet构造函数.
当两种排序方式都存在时,以比较器为主.
比较器比较好,常用 灵活,可扩展
Comparable //该接口强制让实现了它的子类具备比较性.只有一个方法compareTo(To); (例子见文档后)
Comparator: compare(T o1, T o2); equals(Object obj); (例子见文档后)
练习:按照字符串长度排序
à字符串本身具备比较性,但是它的比较方式不是所需要的,只能使用比较器, (例子见文档后)
泛型
在使用java提供的对象时,什么时候使用泛型呢
通常在集合框架中很常见,只要见到< > 就要定义泛型.
其实< > 就是用来接收类型的.
当使用集合时,将集合中要存储的数据类型作为参数传递到 < >中即可.
当类中要操作的引用数据类型不确定的时候,再起定义Object来完成扩展,现在定义泛型来完成扩展.
//泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就应经固定了
为了让不同方法可以操作不同的类型,而且类型还不确定,
那么可以将泛型定义在方法上 他们可同时存在
静态方法不可以访问类上定义的类型,如果静态要操作的数据类型不确定,可以将泛型定义在方法上.
泛型还可以定义在接口上 写法: class InterImpl <T> implements Inter<T>
通配符 ArrayList<?> 表示类型不明确,用?也可理解为占位符,
泛型不能使用类型特有方法,因为类型确定.
泛型的限定: ?extends E: 可以加收E类型或者E的子类型.上限.
?super E: 可以接收E类型或者E的父类型,下限.
TreeSet<E> 代表一个有序的元素为E的树,它其中的一个构造器需要一个Comparator类来比较两个元素,以E为String类时为例,此时的Comparator可以是Comparator<String>,也可以是Comparator<Object>,但Comparator<Integer>就不行,如何表示这样的限制呢?
jdk源代码中是这样的:
public TreeSet(Comparator<? super E> c) //接口Comparator的<
>里面可以为E类型和E的父类,因为父类引用可以接收子类对象.这样就保证了传给构造器的Comparator是可以进行E元素的比较的。
Map (K,V)
该集合存储键值对,一对一对的往里存,而且要保准键的唯一性.
该接口的主要共性方法
1. 添加 put(K key, V value) putAll(Map<? extends K, ? extends V > m)
2. 删除 clear() remove(Object key)
3. 判断 containsValue(Object value) containsKey(Object key) isEmpty()
4. 获取 get(Object key) size() values() entrySet() keyset();
5.
values() 返回此映射中包含的值的
Collection视图。该collection 受映射支持,所以对映射的更改可在此 collection 中反映出来,反之亦然。如果对该 collection 进行迭代的同时修改了映射(通过迭代器自己的remove 操作除外),则迭代结果是不确定的。collection 支持元素移除,通过Iterator.remove、Collection.remove、removeAll、retainAll
和clear 操作可从映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
put() 添加时,相同的键,那么后添加的值会覆盖原有键的对应值,并put方法返回被覆盖的值
keyset() 返回此映射中包含的映射关系的Set视图
将map中的所有键存入Set集合,因为Set具备迭代器.
所有可以迭代方式取出所有的键,再根据get方法,获取每一个键的对应的值.
entrySet() 返回此映射中包含的映射关系的Set视图 返回的数据类型Set(Map.Entry(K,V))
Map.entry也是一个接口,他是Map接口中的内部接口.
先有Map集合,再有Entry(键值关系),且Entry需要直接访问Map中的元素,所以Entry接口封闭在Map接口内部
加static 的接口一定是内部接口,因为只有在成员位置上才能加static 静态修饰符.
练习:”dsfefdsfdfegg”获取该字符串中的字母出现的次数. (例子见文档尾).
map集合被使用是应为具备映射关系
Collections
排序 sort
自然排序 public static <T extends Comparable<? super T>> void sort(List<T> list)
List<T>集合中的元素想要排序,先要进行比较,强制限定元素类型T具有比较性,所以要对泛型<T>进行泛型限定 :它必须实现Comparable接口àT extends Comparable,而Comparable 也可以添加泛型Comparable<? super T>.
比较器排序 : public static <T> void sort(List<T> list, Comparator<? super T> c)
取最大值 :max max(Collection)
binarySearch()二分查找,(调用前先调用sort()方法对集合排序).
如果搜索键包含在列表中,则返回搜索键的索引(可自定义比较器).没找到返回 (-(插入点) - 1).
fill() 将list集合中所有元素替换成指定元素 replacAll()替换 reverse()反转元素, synchronizedList()同步化List.
swap() 交换元素位置, shuffle() 洗牌,打乱顺序
reverseOrder() 排序反转
例子:TreeSet(Collections.reverseOrder())//将集合的自然顺序逆转. TreeSet(Collection.reverseOrder ( Comparator c))//将比较器逆转,在传给TreeSet
Collection.toArray(new String
) 集合转数组
当指定类型的数组长度小于集合的size,那么该方法内部创建一个新数组,长度为集合的size
当指定类型那个的长度大于集合的size,就不会创建了数组,而是使用传进来的数组
为什么集合变数组,限定对元素的操作.只能读,不能删改增.
Array
binarySearch() deepEquals(),还比较数组里的元素(如容器)里元素.可用于多维数组判断
fill()还可指定范围填充数组 sort()还可以局部排序
asList()把数组变成集合,集合可用方法多.(如果数组元素是基本类型,那么会将该数组作为集合中的元素存在.)
增强for循环:
只能获取集合元素,不能对集合进行操作,iterator除了遍历,还可以进行remove操作
如果用ListIterator,还可以在遍历过程中对集合进行增删改查的操作.
传统for和高级for的区别
高级for的一个局限性,必须有遍历的目标,比如循环打印输出一句话,for就不行.
传统for可以定义角标,可操作(判断)角标
//用stack模拟堆栈, (模拟队列类似)
class Stack{
private
LinkedList link;
Stack(){
link = newLinkedList();
}
public
void add(Object o){
link.addFirst(o);
}
public Object get(){
returnlink.removeLast();
}
public
boolean isNull(){
returnlink.isEmpty();
}
}
================================================================
//Comparable例子
class Studentimplements
Comparable {
public Stringname;
public
int age;
public
int compareTo(Object obj) {
if (!(objinstanceof Student))
throw
new RuntimeException("不是学生对象");
Student s = (Student) obj;
if (this.age > s.age)
return 1;
if (this.age == s.age) //如果光比较age,当向TreeSet添加Student元素时,年龄相同的添加不进去
return this.name.compareTo(s.name); //String已经实现了Comparable
return -1;
}
}
//Comparator 例子
class MyComparatorimplements
Comparator{
public
int compare(Object o1,Object o2){
Student s1 = (Student) o1;
Student s2 = (Student) o2;
int num = s1.name.compareTo(s2.name);
if(num==0)
num = s1.age-s2.age;
return num;
}
}
//以字符长度排序Comparator
class StrLenComparatorimplements Comparator<String >{
public
int compare(String o1,String o2){
int num = newInteger(o1.length()).compareTo(new Integer(o2.length()));
if(num==0)
return o1.compareTo(o2);
return num;
}
}
//” dsfefdsfdfegg”获取该字符串中的字母出现的次数.
// 1, 将字符串转换成字符数组,因为要对每一个字母进行操作
// 2, 定义一个map集合,因为打印结果的字母有顺序,所以使用treeMap集合
// 3,遍历祖父数组,将每一个字母作为见去查map集合,如果返回null,将该字母和1存入到map集合中.
//如果返回不是null,说明该字母在map集合已经存在并有对应次数,那么就获取盖子出并进行自增,然后再存入map
public
static void charCount(Stringstr){
char[] chs = str.toCharArray();
TreeMap<Character,Integer> tm = newTreeMap<Character,Integer>();
int count = 0;
for(int x = 0; x<chs.length;x++){
Integer value = tm.get(chs[x]);
if(value!=null)
count = value;
count++;
tm.put(chs[x], count);
count = 0;
/*if(value==null){
tm.put(chs[x], 1);
}
else{
value = value + 1;
tm.put(chs[x], value);
}*/
}
System.out.println(tm);
}
//二分查找
public
static int halfSearch(List<String> list,String key){
int max, min,mid;
max = list.size()-1;
min=0;
while(min<=max){
mid = (max+min)>>1;// /2;
String str = list.get(mid);
int num = str.compareTo(key);
if(num>0)//如果mid大于key,那么max=mid-1;
max = mid-1;
else
if(num<0)//如果mid小于key,那么min=mid+1;
min =mid +1;
else
//如果mid==key,找到了返回值
return mid;
}
return min;//如果没找到,返回插入点;
}
相关文章推荐
- 黑马程序员_Java学习日记10_集合框架2
- 黑马程序员_API集合框架
- 黑马程序员---集合框架
- 黑马程序员——Java基础之集合框架
- 黑马程序员----集合之集合框架
- 黑马程序员——集合框架
- 黑马程序员:JAVA集合框架知识点二
- 黑马程序员_集合框架
- 黑马程序员——JAVA笔记——集合框架5——Collections、Arrays
- 【黑马程序员】集合框架(四)Collections 第十八天
- 黑马程序员——JAVA基础拾遗之泛型和集合框架(二)
- 黑马程序员_集合框架(Collection Framework)介绍及常用方法
- 黑马程序员——集合框架之List
- 黑马程序员_集合框架概述
- 黑马程序员——集合框架5:集合工具类
- 黑马程序员————集合框架1(day14)
- 黑马程序员_JAVA笔记15——集合框架(泛型)
- 黑马程序员_学习笔记集合框架(I)
- 黑马程序员——>第十六天<集合框架(Map)>
- 黑马程序员——集合框架(一)