黑马程序员__集合

毕老师视频 day14-day17

1.集合类:

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

  • 存储方式有:数组、集合类2种

数组和集合类都是容器,不同之处:

  • 数组长度是固定的,集合长度是可变的。
  • 数组可以存储基本数据类型,集合只能存储对象。

集合类的特点:

  • 只用于存储对象;  集合长度是可变的;  集合可以存储不同类型的对象。

为什么会出现很多容器:

  • 因为每一个容器对数据的存储方式都不同, 这个存储方式就是数据结构。

2.  集合框架  

黑马程序员__集合

 黑马程序员__集合

  Collection接口的共性方法:

  • add方法的参数类型是Object,以便接收任意类型的对象;
  • 集合中存储的都是对象的引用。
        //创建一个集合容器,使用Collection接口的子类  ArrayList
        ArrayList al = new ArrayList();
        al.add("212334");
        al.add("japa");
        al.add("212");
        al.add("japa");//1.追加
        System.out.println(al);
        System.out.println(al.size());  //2.大小
        
        
        al.remove("japa"); //3.删除
        System.out.println(al);
            
        System.out.println(al.contains("japa"));//4.查询
        System.out.println(al.isEmpty());  //5.空否
        
    //    System.out.println(al.clear());  //6.清空
        System.out.println(al);
        
        ArrayList bl = new ArrayList();
        bl.add("japa");
        bl.add("japa1");
        bl.add("japa2");
    //    System.out.println(al.retainAll(bl));  //al保留交集部分,bl不变
        System.out.println(al.removeAll(bl));  //al移除交集部分,bl不变
        System.out.println(al);
        System.out.println(bl);

3. 迭代器:

  • 集合的取出元素的方式。

取出方式被定义成了集合的内部类,这样它就可以直接访问集合内容的元素。

每个容器的数据机构不同,所以取出方式的细节也不同,但是有共性内容: 判断和取出

这些内部类都符合一个规则: 接口 Iterator

然后通过集合获取取出方式的对象?  通过每个集合类定义的  iterator() 方法

接口Iterator 只有3个方法:

boolean hasNext()   // 如果仍有元素可以迭代,则返回 true。
E next()   //使迭代器移至下一个位置,返回迭代器经过的对象
void remove()  //删除迭代器左边的对象

    Iterator it = al.iterator();
        System.out.println(it.hasNext());  
        System.out.println(it.next());  //返回第一个对象,
        it.remove();              //删除第一个对象 
        System.out.println(it.next());  //返回第二个对象
        System.out.println(al);
   

4.  Collection接口的2个常见子接口:   List接口     Set接口

List   元素是有序的,元素可以重复。因为该集合体系有索引。

Set   元素是无序的,元素不可以重复。

List集合特有方法:

//
void     add(int index, E element)  
boolean addAll(int index, Collection<? extends E> c)  
//
E remove(int index)   //返回被删掉的数据
//
E set(int index, E element)  //返回被替代的数据 
//
E get(int index)  
List<E>  subList(int fromIndex, int toIndex) 
ListIterator<E>  listIterator(int index) 
int indexOf(Object o)

List集合特有的迭代器:  ListIterator

在迭代时,不可以通过集合对象的方法操作集合中的元素,会发生异常。 所以只能用迭代器的方法操作元素。

Iterator提供的方法有限,如果需要添加、修改操作,需要子接口 ListIterator。

 void add(E e)    //加到迭代器当前的位置,并使其右移一位
 
 boolean hasNext() 
 boolean hasPrevious() 
 
 E next()       //迭代器右移一位,返回经过的对象
 E previous() //迭代器左移一位,返回经过的对象
 
 int nextIndex()    //迭代器不动,返回迭代器右边的角标
 int previousIndex() 
        
 void remove()   //移除由 next 或 previous 返回的最后一个元素
 void set(E e)   //替换由 next 或 previous 返回的最后一个元素

        lit.next();   //迭代器在第一个与第二个之间    
        //lit.set("不是");
        lit.remove(); //移除第一个对象
        System.out.println(al);

            lit.next();
        lit.next();
        lit.previous();  //迭代器也在第一个与第二个之间        
        //lit.set("不是");
        lit.remove();  //移除第2个对象
        System.out.println(al);

5.  List接口有3个常见的子类对象:  ArrayList   LinkedList   Vector

  • ArrayList 底层数据结构使用的数组结构:  查询速度快,增删速度慢。   线程不同步
  • LinkedList 底层数据结构使用的链表结构: 查询速度慢,增删速度快。   线程不同步
  • Vector 底层是数组数据结构, 是线程同步的。    1.0有的,1.2开始被ArrayList替代

(ArrayList 默认长度10,超出后延迟50%;  Vector默认长度10,超出后延迟100%)

    
    /*
    枚举是Vector特有的取出方式。1.0时只有这种方式。
    因为枚举名称过长,已经被迭代器取代了
    */
        Vector  v = new Vector();
        v.add("kaka1");
        v.add("kaka1");
        v.add("kaka1");
        v.add("kaka1");
        for( Enumeration enu = v.elements();enu.hasMoreElements();)
            System.out.println(enu.nextElement());

6.  LinkedList 特有方法:

 void addFirst(E e) 
 void addLast(E e)     //将指定元素添加到此列表的结尾。 
 E getFirst()  //返回此列表的第一个元素。 
 E getLast()   //返回此列表的最后一个元素。 
 E removeFirst() //返回并移除此列表的第一个元素。 
 E removeLast()  //返回并移除此列表的最后一个元素。没有元素抛出异常

//JDK1.6版本后出现了替代方法,不再抛出异常
 boolean offerFirst(E e) //在此列表的开头插入指定的元素。 
 boolean offerLast(E e) //在此列表末尾插入指定的元素。 
 
 E peekFirst() //获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。 
 E peekLast()  //获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。 
 
 E pollFirst() //获取并移除此列表的第一个元素;如果此列表为空,则返回 null。 
 E pollLast()  //获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。 

7.  Set集合的方法和 Collection 是一致的。

HashSet底层数据结构是哈希表。 线程不同步。 HashSet如何保证元素的唯一性?:

  • 通过元素Object的方法 hashCode()  和 equals() 来完成。
  • 先判断hash值是否相等,如果哈希值相同,才会调用 equals 方法。
  • 因此,对应自定义对象,通常要重写hashCode 和 equals 方法。
  • HashSet的寻找和删除方法,contains(obj)  remove(obj) 依赖的也是元素的hashCode和equals方法。
  • ArrayList判断元素是否相同只调用equals().

TreeSet  可以对Set集合中的元素进行排序。 底层数据结构是二叉树。  如何保证元素唯一性?:

  • compareTo  方法。  contains(obj)、 remove(obj)也调用的compareTo方法。
  • TreeSet排序的第一种方式:  让元素具备可比较性, 元素需要实现 Comparable 接口,重写 compareTo 方法。
  • 第二种方式: 元素不具备比较性时,需要让集合自身具备比较性。
  • 需要定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数
  • 当2种比较方式都存在时,会自动调用比较器方法。
class Person implements Comparable
{
    private String name;
    private int age;
    Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public int compareTo(Object obj)
    {
        System.out.println("比较");
        if(!(obj instanceof Person))
            throw new RuntimeException("对象类型错误");
        Person p = (Person) obj ;
        if(this.age>p.age)
            return 1;
        if(this.age==p.age)
        {
            return this.name.compareTo(p.name);
        }
        return -1;
    }
    

}

class myCompare implements Comparator
{
    public int compare(Object o1,Object o2)
    {
        Person p1 = (Person) o1;
        Person p2 = (Person) o2;
        return 1;
    }
}

8.  泛型: JDK1.5之后出现的新特性,用于解决安全问题,是一个安全机制。 好处:

  • 将运行时期可能出现的ClassCastException异常转移到了编译时期,方便程序员解决问题
  • 避免了强制类型转换。

泛型格式: 通过 <>  来定义要操作的引用数据类型。

什么时候使用泛型??

  • 通常在集合框架中很常见,只要见到<>就要定义泛型。
  • 在使用集合时,将集合要存储的数据类型作为参数传递到<>中即可。
  • 接口 Comparable 、 Comparator 都带泛型。

9.泛型类:

class Util<T>  //泛型类
{
    private T t;
    public void setObject(T t)
    {
        this.t = t;
    }
    public T getObject()
    {
        return t;
    }
}

什么时候定义泛型?

  • 当类中要操作的引用数据的类型不确定时,
  • 早期用Object来完成扩展,现在用泛型来完成扩展。

泛型方法:

  • 泛型类定义的泛型,在整个类都有效 
  • 当泛型类的实例指出了要操作的数据类型,所有方法的能操作的数据类型就固定了。
  • 为了让不同的方法可以操作不同类型的不确定数据, 可以把泛型定义在方法上
class Util <QQ>   //可以在泛型类中定义泛型方法表示不同泛型
{
    private QQ q;
    public <T> void setObject(T t)
    {
        
    }
    //泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。
    //和泛型类一样,可以声明多个泛型,用逗号隔开。
    public static <T,K> T  getObject(K key)
    {
        return t;
    }
}

静态方法泛型:

  • 静态方法不可以访问类上定义的泛型
  • 静态方法只能使用方法自身上定义的泛型
class Util<T>  //泛型类
{
    private T t;
    public static <K> void method(K x)
    {
        System.out.println(x);
    }
}

泛型定义在接口上:

interface Inter <T>
{
    void show(T t);
}
class Demo1 implements Inter<String>
{
    public void show(String str)
    {
        System.out.println(str);
    }
}
class Demo2<T> implements Inter<T>  //2个T
{
    public void show(T t)
    {
        System.out.println(t);
    }

}
class Main
{
    public static void main(String [] args)
    {
        Demo1 dm1 = new Demo1();
        dm1.show("haha");
        
        Demo2<Integer> dm2 = new Demo2<Integer>();
        dm2.show(007);
        }
}

? 通配符

泛型的限定:

  •  ?  extends E    :可以接收E类型或者E的子类型,  上限
  • ?  super E        :  可以接收E类型或者E的父类型 ,  下限
class Person
{
    private String name;
    Person(String str)
    {
        this.name = str;
    }
    public String getName()
    {
        return name;
    }
}
class Student extends Person 
{
    Student(String str)
    {
        super(str);
    }
}
class Woker extends Person
{
    Woker(String str)
    {
        super(str);
    }

}

class Comp implements Comparator<Person> //super泛型限定,可以比较Person的子类 
{
    public int compare(Person s1, Person s2)
    {
        return s1.getName().compareTo(s2.getName());    
    }
}


class Main
{
    public static void main(String [] args)
    {
        //super泛型限定,定义比较器时,可以定义Student或者它的父类 
        TreeSet<Student> ts = new TreeSet<Student>(new Comp());
        ts.add(new Student("学生11"));
        
        TreeSet<Person> ts2 = new TreeSet<Person>(new Comp());
        ts2.add(new Person("人11"));
        

        
        
    
        ArrayList<Person>  ala = new ArrayList<Person>();
        ala.add(new Person("人1"));
        ala.add(new Person("人2"));
        printAl(ala);
        
        ArrayList<Student>  alb = new ArrayList<Student>();
        alb.add(new Student("学生1"));
        alb.add(new Student("学生2"));
        printAl(alb);
                
        ArrayList<Woker>  alc = new ArrayList<Woker>();
        alc.add(new Woker("工人1"));
        alc.add(new Woker("工人2"));
        printAl(alc);        
        
    }
    //extends泛型限定,定义参数时,可以定义Person或者它的子类 
    public static void printAl(ArrayList<? extends Person> al)
    {
        for(Iterator<? extends Person> it=al.iterator(); it.hasNext();)
        {
            System.out.println(it.next().getName());
        
        }
    }    
}

10. Map集合

  • Map集合表示映射关系, 键-->值,  键不可重复
//添加
V put(K key, V value) // 在映射中添加传入的键和值, 如果映射中已经包含了一个该键的映射,则旧值被替换。
void putAll(Map<? extends K,? extends V> m) //从指定映射中将所有映射关系复制到此映射中

//删除
V remove(Object key) //如果存在一个键的映射,则将其从此 Map 中移除。
void clear()   //从此映射中移除所有映射关系。 
 
 //查询
boolean containsKey(Object key)     //如果此映射包含指定键的映射关系,则返回 true。 
boolean containsValue(Object value) //如果此映射将一个或多个键映射到指定值,则返回 true。 
boolean isEmpty() //如果此映射未包含键-值映射关系,则返回 true。 
 
 //获取
V get(Object key) // 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 
int size()         //返回此映射中的键-值映射关系数。  
Collection<V> values()  //返回此映射中包含的值的 Collection 视图。 
  
Set<Map.Entry<K,V>>   entrySet() //返回此映射中包含的映射关系的 Set 视图。 
Set<K>  keySet()      // 返回此映射中包含的键的 Set 视图。

|-- Hashtable 底层数据结构是哈希表,  不可以存入null , 线程同步。

  •    键必须实现HashCode和equals方法

|-- HashMap  底层数据结构也是哈希表, 允许使用 null 键、 null 值, 线程不同步。

|-- TreeMap  底层数据结构是红黑树。   线程不同步

  • 可以用于给map集合中的键进行排序

Set集合的底层实现使用了Map集合

11. Map集合有2种取出方式:

  • keySet 将Map中所有的键存入Set集合,使用Set集合的迭代器取出键,再根据get(key)方法取出值
  • entrySet  将Map集合中的映射关系存入 Set集合,使用Set集合的迭代器取出关系。这个关系的数据类型是:  Map.Entry
  • Map.Entry 是Map 接口的内部接口
class Student implements Comparable<Student>
{
    private String name;
    private int age;
    Student(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    
    public int hashCode()
    {
        return name.hashCode()+age*33;
    }
    public boolean equals(Object obj)
    {
        if(!(obj instanceof Student))
            throw new RuntimeException("类型异常");
        Student stu = (Student) obj;
        return this.name.equals(stu.name) && this.age==stu.age;
    }
    public int compareTo(Student stu)
    {
        if(this.name.compareTo(stu.name)!=0)
            return this.name.compareTo(stu.name);
        return this.age-stu.age;
    }
    public String toString()
    {
        return name+"::"+age;
    }
    
}
class Main
{
    public static void main(String [] args)
    {
        HashMap<Student,String> hm = new  HashMap<Student,String>();
        hm.put(new Student("any",21),"beijing");
        hm.put(new Student("boy",29),"fffkk");
        hm.put(new Student("gir",25),"zhongng");
        hm.put(new Student("zip",18),"gasotan");
    
        //第一种取出方式
        Set<Student> keyset = hm.keySet();
        for(Iterator<Student> it=keyset.iterator(); it.hasNext();)
        {
            Student stu = it.next();
            String adr = hm.get(stu);
            System.out.println(stu+"--"+adr);
        }
        
         //第二种取出方式
         Set<Map.Entry<Student,String>> es = hm.entrySet();
        for(Iterator<Map.Entry<Student,String>> it=es.iterator(); it.hasNext();)
        {
            Map.Entry<Student,String> me = it.next();
            Student stu = me.getKey();
            String adr = me.getValue();
            System.out.println(stu+"==="+adr);        
        }
    }
}

 12.Collections

  •  Collections是对集合框架的一个工具类。它里边的方法都是静态的,不需要创建对象。并未封装特有数据。
  •  在Collections工具类中大部分方法是用于对List集合进行操作的,如比较,二分查找,随机排序等。

Collections和Collection的区别:

  •  Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。

         它有两个常用的子接口:

                List:对元素都有定义索引。有序的。可以重复元素。        

                Set:不可以重复元素。无序

  • Collections是集合框架中的一个工具类。该类中的方法都是静态的。提供的方法中有可以对list集合进行排序,二分查找等方法

       通常常用的集合都是线程不安全的。因为要提高效率。如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。

//查找
T max(Collection<? extends T> coll);//根据集合的自然顺序,获取coll集合中的最大元素
T max(Collection<? extends T> coll,Comparator<? super T> comp);//根据指定比较器comp的顺序,获取coll集合中的最大元素
static <T> int  binarySearch(Lsit<? extends Comparable<? super T>> list,T key);//二分法搜索list集合中的指定对象
//替换
voidfill(List<? super T> list, T obj);//将list集合中的全部元素替换成指定对象obj
boolean replaceAll(List<T> lsit,T oldVal,T newVal);//用newVal替换集合中的oldVal值
void swap(Listlist,int i,int j);/在指定列表的指定位置处交换元素
//排序:
void shuffle(List<?> list);//使用默认随机源对list集合中的元素进行随机排序
void sort(Lsit<T> list);//根据自然顺序对list集合中的元素进行排序
void sort(List<T> lsit,Comparator<? super T> c);//根据指定比较器c的排序方式对list集合进行排序
//反转
reverse(List<?> list);//反转list集合中元素的顺序
Comparator reverseOrder();//返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序
Comparator reverseOrder(Comparator<T> cmp);//返回一个比较器,强行逆转了指定比较器的顺序
//同步的集合
List<T>  synchronizedList(List<T> list);//返回支持的同步(线程安全的)List集合
Map<K,V> synchronizedList(Map<K,V> m); //返回支持的同步(线程安全的)Map集合

13. Arrays

  • Arrays是用于操作数组的工具类。里边的方法也全是静态的。不需要创建对象。
  •  把数组变成List集合的好处:可以使用集合的思想和方法来操作数组中的元素。如:contains,get,indexOf,subList等方法。

方法:

Lsit<T>  asList (T... a);//将数组转换为集合,返回一个受指定数组支持的固定大小的列表
  • 将数组转换成集合,不可使用集合的增删方法,因为数组的长度是固定的。如果进行增删操作,则会产生UnsupportedOperationException的编译异常。
  • 如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。
  • 如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
binarySearch()//二分查找方法
fill()      //替换方法
sort()   //排序方法等

集合也可以转换成数组:

Collection接口中的toArray方法。

        <T> T[]  toArray(T[] a)  //将集合变为指定类型的数组。
  • 指定类型的数组到底要定义多长呢?

        当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组。长度为集合的size。

         当指定类型的数组长度大于了集合的size,就不会新创建了数组。而是使用传递进来的数组。

         所以创建一个刚刚好的数组最优。

  • 为什么要将集合变数组?

         为了限定对元素的操作。不需要进行增删了。

14. 高级for循环:

  for(数据类型变量名 :被遍历的集合(collection)或者数组) {执行语句}

特点:

  • 对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。可以看作是迭代器的简写形式。
  •   迭代器除了遍历,还可以进行remove集合中元素的动作。如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。