您的位置:首页 > 职场人生

黑马程序员: 集合框架

2013-03-23 16:30 204 查看
-------
android培训java培训、期待与您交流! ----------




集合框架

为什么出现集合类?

              面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式.

  数组和集合类同时容器,有何不同?

              数组虽然也可以存储对象,但长度固定;集合长度可变,数组可以储存基本数据类型,集合只能存储对象,

集合类的特点: 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象.

各种集合不断向上抽取,形成集合框架.



每个容器的存储方式都有不同,这个存储方式叫数据结构

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;//如果没找到,返回插入点;
    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: