黑马程序员----java集合

来源:互联网 发布:在哪看淘宝达人的帖子 编辑:程序博客网 时间:2024/05/17 23:00

---------------------- ASP.Net+Unity开发 .Net培训 期待与您交流! ----------------------


为什么会出现这么多的容器呢?

    因为每一个容器对数据的存储方式都不同。
    这个存储方式称之为:数据结构。

Collection

    顶级接口
    
    public boolean add(E e)
    public boolean addAll(Collection<? extends E> c)

    public void clear()    ;//清空

    public boolean remove(Object o)

    public boolean removeAll(Collection<?> c)
        //去除指定集合c与本身集合都有的元素,去交集

    public boolean contains(Object o)
    public boolean isEmpty()
    public int size()    ;

    public boolean retainAll(Collection<?> c)
        //仅保留此collection中那些也包含在指定collection中的所有元素
        //取交集

    public Object[] toArray()

    public Iterator<E> iterator()
        迭代器

    
1,add方法的参数类型就是Object,以便于接受任意类型对象。
2,集合中存储的都是对象的引用

Iterator接口

    hasNext()
    next()
    remove()

    把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素。
    那么取出方式就被定义成了内部类;而每一个容器的数据结构不同,所以取出的
    动作细节也不一样。但是都有共性内容[就是判断和取出]。那么可以将这些共性抽取出来。
    这些内部类都符合一个规则。抽象成一个接口:Iterator接口。
    如何获取集合的取出数据对象呢?
    通过一个对外提供的方法: iterator();

    迭代器的for循环
    for(Iterator it=arrayList.iterator(); it.hasNext(); ){
        System.out.println(it.next());
    }


Collection 的常见子接口:

        List,        Set
    |List:集合的特点:元素是有序的,元素是可以重复的,
    |                因为该集合体系有索引。
    |Set:集合的特点:元素是无序的,元素是不可以重复的,
    |                该集合体系没有索引。

List中的特有方法:

    凡是可以操作脚标的方法都是该体系特有的方法:
    增
    public void add(int index, E element);
    public void addAll(index, Collection)
    删
    public E remove(int index)
    改
    public  set(index,element)
    查
    public E get(int index)
    public List<E> subList(int fromIndex, int toIndex);
        返回列表中指定的fromIndex(包括)和toIndex(不包括)之间的部分视图。
    public int indexOf(Object o)
        获取元素的脚标
    public int lastIndexOf(Object o)
        
    public ListIterator<E> listIterator(int index)
        列表迭代器

    在迭代器迭代过程中,如果对数据进行了增删改操作,会出现
        java.util.ConcurrentModificationException:(并发修改异常:当方法检测到对象的并发修改,但不允许这种修改,抛出此异常.)
    所以在迭代过程中,只能用迭代器的remove() 方法

ListIterator(列表迭代器)

    list集合特有的迭代器。ListIterator是Iterator的子接口
    在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生并发修改异常。
    所以,在迭代时,只能使用迭代器的方法来操作元素,可以Iterator方法是有限的,

    如果想要其他的操作,如添加,修改等,就需要使用其子接口,ListIterator;
    该接口只能通过List集合的 方法 listIterator() 来获取

    public boolean hasNext()
    public E next()
    public boolean hasPrevious()
    public E previous()
    public int previousIndex()
    public int nexIndex()

    public void add(E e)
    public void remove()
    public void set(E e)

List常见实现类

    |ArrayList:        底层使用的是数组结构;特点:查询速度很快,增删改的速度慢。不同步的
    |LinkedList:    底层使用的是链表数据结构;特点:查询速度慢,增删改的速度快。不同步
    |Vector:        底层使用是数组结构;Vector是同步的。被ArrayList替代了

Vector 中的一个比较特别的方法:
    public Enumeration elements();
        //枚举就是Vector特有的取出元素遍历的方式,枚举和迭代器的方式很像
        //其实枚举和迭代是一样的。因为枚举的名称以及方法的名称都过长,所以
        //被迭代器取代了。
    eg:        Enumeration en = vector.elements();
            while(en.hasMoreElements()){
                System.out.println(en.nextElement());
            }

LinkedList的一些特有的方法
        addFirst();
        addLast();
        getFirst();    //获取但是不删除第一个元素,如果集合中没有元素时会抛出异常NoSuchElementException。
        getLast();    //获取但是不删除最后一个元素,如果集合中没有元素时会抛出异常。
        removeFirst();    //获取并且删除第一个元素,如果集合中没有元素时会抛出异常。
        removeLast();    //获取并且删除最后一个元素,如果集合中没有元素时会抛出异常。
        
        版本1.6才出现这个方法
        offerFirst()            添加元素
        offerLast()
        peekFirst()
        peekLast()
        public E pollFirst();    获取并移除第一个元素,如果此列表为空,则返回null.
        public E pollLast();
    
重点:1.问题:使用LinkedList模拟一个堆栈或者队列数据结构
        堆栈:先进后出
        队列:先进先出
     2.问题:去除ArrayList集合中的重复元素

List集合判断元素是否相同,使用的是元素的equals方法来进行判断。


Set:

  元素是无序(存入和取出的顺序不一定一致),而且元素不可以重复

    Set集合的方法和Collection的方法是一致的。
    |HashSet:        底层的数据结构是哈希表,线程是非同步的。
    |TreeSet:                

    HashSet存储自定义对象,自定义对象需要重写 equals()方法,hashCode()方法。
    用于保证存入set集合中的元素的惟一性。
        当一个对象存入到集合中时,先用hashCode() 方法确定 hashCode值,
        如果hashCode值不同,不会调用equals()方法;
        如果hashCode值相同,才会调用equals()方法来判断两个对象是否相同,如果相同则不放入到集合中。

注意细节:
    public int hashCode(){}        //返回的是int
    public boolean equals(Object obj)    //接收的参数是Object

    hashSet的contains(), remove() 等方法的判断和删除依据,
    依然是先判断hashCode(),然后调用equals()方法


TreeSet:

        可以对Set集合中的元素进行排序,底层的数据结构是二叉树。

    没有指定排序方式时是按照自然顺序来排序。

    自定义对象 存入到TreeSet中时,可能会抛出java.lang.ClassCastException异常。
    自定义对象需要实现java.lang.Comparable接口,才不会出现异常。
    
    public interface Comparable<T>
        此接口强行对实现它的每一个类的对象进行整体排序。这种排序被称为类的自然排序。
        类的compareTo()方法被称为它的自然比较方法。
        public int compareTo(Object obj){}
        //当通过compareTo()方法判断两个对象相等时,会被认为是重复元素,会导致该对象无法存入到集合中。

        注意:
            排序时,当主要条件相同时,一定要判断一下次要条件。

    TreeSet排序的第一种方式:让元素自身具备比较性。
        元素需要实现Comparable接口,覆盖compareTo方法。
        这种方式也称为元素的自然顺序,或者叫做默认顺序。

    TreeSet排序的第二种方式:
        当元素自身不具备比较性时,或者具备的比较性不是所需要的。
        这时就需要让集合自身具备比较性。
        在集合初始化时,就有了比较方式。
        构造器
        TreeSet(Comparator<? super E> comparator )
            构造一个新的空TreeSet,它根据指定比较器进行排序。

    
    当2种排序都存在时,以集合的比较器为主。

练习:    1,按照字符串长度排序。


泛型:

     jdk1.5出现的新特性,用于解决安全问题,是一个类型安全机制。

    通常在集合框架中很常见
    当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

    泛型的好处:
        1.将运行时期出现的问题ClassCastException,转移到了编译时期,
        方便了程序员解决问题,让运行时的问题减少,程序更安全
        2.避免了强制转换的麻烦
    泛型的格式:通过<>来定义要操作的引用类型。

如何在自定义类中使用泛型?
    1.泛型可以定义在类上
        public class Demo<T>

    2.为了让一个类中的不同方法可以操作不同类型对象,而且类型不确定。
    那么可以将泛型定义在方法上。
        public <T> void show(T t)

    3.泛型类和泛型方法可以应用在同一个类中,只要定义不同的标识类型

    4.注意:泛型只有在确定对象实例时才能确定类型;静态方法不可以访问类上定义的泛型;
            如果静态方法操作的应用数据类型不确定,可以将泛型定义在静态方法上。
        //静态方法定义泛型注意格式
        public static <T>  void method(T t){}

    5.泛型定义在接口上
        public Interface Inter<T>{
            void show(T t);
        }
        然后定义类实现接口
        class InterImpl implements Inter<String>{
            public void show(String t){...}
        }
        如果定义类实现接口时也不能确定类型,那么需要写成
        class InterImpl<T> implements Inter<T>{
            public  void show(T t){.......}
        }

泛型限定

    泛型中的通配符    :    ?
                    ? extends E    E以及E的继承类;上限
                    ? super E        E以及E的父类;下限
泛型限定是用于泛型扩展用的


Map集合

    1,存储键值对。而且要保证 键 的惟一性。
    2,    map集合没有迭代器
    Map集合的通用方法
        增
            public V put(K key, V value);        
                //存入键值对,
                //如果添加时出现相同的键,那么后添加的值会覆盖原来键对应值,返回原来的值。
                //否则返回null
             public void putAll(Map<? extends K,  ? extends V> m)
        删
            public void clear();
             public V remove(Object key)
        判断
            public boolean containsKey(Object key)   //是否包含指定的 键                    
            public boolean containsValue(Object value)
            public boolean isEmpty()
        获取
            public V get(Object key)    //返回指定键所映射的值;此映射不包含该键,返回null
            public int size()    
            public Collection<V> values()    //得到所有的值
            public Set<Map.Entry<E,V>> entrySet()    
                //返回此映射包含的映射关系的Set视图
                //Map的内部接口:Map.Entry
                //Map.Entry其实Entry是Map接口中的一个静态内部接口。    
                //    public static interface Map.Entry<K,V>
                //    能加static修饰的接口一定是内部接口
            public  Set<K> keySet()        
                //将map中所有的键存入一个set集合中
                

Map 常用的子类

    |--Hahstable
    |--HashMap
    |--TreeMap
    
Set底层就是使用的Map集合。

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, Serializable
    1.底层是哈希表数据结构,
    2.不可以存入null键 null值
    3.线程同步的
    4.用作键的对象必须实现hashCode方法和equals() 方法
    
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable,Serializable
    1.底层是基于哈希表的数据结构
    2.允许使用null健 ,null值
    3.线程非同步的
    4.不保证映射的顺序,特别是它不保证该顺序恒久不变
    5.注意:除了非同步和允许使用null之外, HashMap和hashtable大致相同;
            jdk1.2才出现hashmap,jdk1.0 就有hashtable了

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, Serializable
    1.基于红黑树的NavigableMap实现;底层就是二叉树数据结构
    2.线程不同步。可以用于给Map集合中的键进行排序。


hashMap集合中键可以为null,而且唯一。
    可以通过get方法的返回值来判断一个键是否存在。
        
对hashMap的遍历
    //1.第一种遍历方式    //获得所有键的Set集合    Set<String> keySet = map.keySet();    //有了Set集合,就可以取得迭代器    Iterator<String> it = keySet.iterator();    while(it.hasNext()){        System.out.println("value:" +hashMap.get(it.next()));    }


    //2.第二种遍历方式
    Set<Map.Entry<String,String>> entrySet = map.entrySet();    Iterator<Map.Entry<String, String>> it = entrySet.iterator();    while(it.hasNext()){        Map.Entry<String,String> me =it.next();        String key = me.getKey();        String value= me.getValue();        System.out.println("key :" + key + ", value" + value);    }



练习1:
    每一个学生都有对应的归属地
    学生 Student,地址String
    学生属性:姓名,年龄
    注意:姓名和年龄相同的视为一个学生
    保证学生的惟一性
    1,描叙学生
    2,定义map容器,将学生作为键,地址作为值,存入。
    3,获取map集合中的元素。

//知识点:equals方法中抛出类型不匹配异常
   // ..    public boolean equals(Object obj){        if(! (obj instanceof Student)){            throw new ClassCastException("类型不匹配");        }    }   // ...



    自定义的Student类需要实现hashCode(),equals()方法,并需要Comparable接口

练习2:
    对学生对象的年龄进行升序排序
    因为数据是以键值对形式存在的。
    所以要使用可以排序的Map集合。TreeMap。

练习3:
    “sadfsadgsadgsa” 获取该字符串中的字母出现的次数。
    希望打印结果:a(1)c(2)....

Map集合的扩展知识
    map集合被使用是因为具备映射关系
    map集合的嵌套
    
    例如;一个学校有多个教室,一个教室有多个学生


集合的工具类    Collections

    
    方法:
        public static <T extends Comparable<? super T>> void sort(List<T> list)
            //根据元素的自然顺序对指定列表按升序进行排序。

        public static <T> void sort(List<T> list, Comparator<? super T> c)
            //根据指定比较器产生的顺序对指定列表进行排序。

        public static <T extends Object & Comparable<? super T>>  T max(Collection<? extends T> coll)
            //根据元素的自然顺序返回给定collection的最大元素。
        
        public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key)
            //使用二分搜索法搜索指定列表,以获得指定对象。
            //但凡使用binarySearch,集合必须是已经排好序的。
            //返回指定的元素的索引

        public static <T> void fill(List<? super T> list, T obj);
            //使用指定元素替换指定列表中的所有元素
        
        public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal);
            //使用另一个值替换列表中出现的所有某一指定值

        public static void reverse(List<?> list)
            //反转指定列表中元素的顺序,
        
        public static <T> Comparator<T> reverseOrder()
            //返回一个比较器,它强行逆转实现了Comparable接口的对象collection的自然顺序。
            //eg:
                TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());
        
        public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
            //返回一个比较起,它强行逆转指定比较起的顺序。
            //如果指定比较起为null,则此方法等同于reverseOrder
        public static void swap(List<?> list, int i, int j)
            //在指定列表的指定位置处交换元素
        public static void shuffle(List<?> list)
            //随机对列表中的元素的位置重新洗牌


        //让集合支持同步
        public static <T> Collection<T> synchronizedCollection(Collection<T> c)
            //返回指定collection支持的同步(线程安全的)collection
        public static <T> List<T> synchronizedList(List<T> list)
            //返回指定列表支持的同步(线程安全的)列表
        public static <E,V> Map<E,V> synchronizedMap(Map<K,V> m)
        .........

Arrays: 用于操作数组的工具类,里面都是静态方法

    
        public static <T> T[] copyOfRange(T[] original, int from, int to)
            //将指定数组的指定范围复制到一个新数组,不包括to索引位置的元素
        
        public static boolean equal(Object[] a, Object[] a2);
            //如果两个指定的Objects数组彼此相等,则返回true;

        public static boolean deepEquals(Object[] a1, Object[] a2)
            //如果两个指定数组彼此是深层相等的,则返回true
        
        public static void fill(Object[] a, Object val);
            //将指定的Object应用分配给指定Object数组的每个元素。
        public static void fill(Object[] a, int fromIndex, int toIndex, Object val);
            // 将指定的Object应用分配给指定Object数组的指定范围的元素。
        
        public static void sort(T[])

        public satic String toString(T[] a)
            //返回指定数组内容的字符串表现形式

        public static <T> List<T> asList(T... a)
            //返回一个受指定数组支持的固定大小的列表,将数组转换成List集合
            //注意:将数组变成集合,不可以使用集合的增删方法,因为数组的长度是固定的
            //否则会抛出异常UnsupportedOperationException    
            //
            //如果数组中的元素都是对象。那么变成集合时,数组中的元素就直接转成集合中的元素。
            //如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。

    //集合转换成数组
    Colleciton中的
    public <T> T[] toArray(T[] a);
        //接受一个数组参数,指定类型的数组的长度要定义多长呢?
        //当指定类型的数组长度小于集合的size,那么该方法内部会创建一个新的数组,长度为集合的size
        //当指定类型的数组的长度大于集合的size,就不会新创建数组了,而是使用传递进来的数组。
        eg:    ........
            String[] arr = arrayList.toArray(new String[arrayList.size()]);
            System.out.println(Arrays.toString(arr));

        //为什么要将集合转换成数组?
            为了限定对元素的操作。

1.5版本开始一个新的接口      public interface Iterable<T>   增强for

    //实现这个接口允许对象成为“foreach”语句的目标。
    //接口的方法:
    public Iterator<T> iterator()
        //返回一个在一组T类型的元素上进行迭代的迭代器

传统for和高级for有什么区别呢?
    高级for有一个局限性,高级for必须有一个被遍历的目标。

建议遍历数组的时候,依然使用传统for,可以使用索引。

1.5出现的一个新特性:    可变参数
                        其实就是数组参数的简写形式。
                        不用每一次都手动的建立数组对象。
                        只要将要操作的元素作为参数传递即可
                        隐式的将这些参数封装成数组。
                注意:可变参数一定要定义在参数列表的最后边。
    
1.5版本的新特性: 静态导入
        静态导入导入的全部是一个类中的静态成员(静态方法,静态属性.......)
    eg:    import static java.util.Arrays.*;    //导入的是Arrays这个静态工具类中的所有静态成员
        import static java.lang.System.*;        //导入了System类中的所有静态成员
        //当类名重名时,需要指定具体的包名

        //当方法重名时,需要指定所属的对象或者类。


---------------------- ASP.Net+Unity开发 .Net培训 期待与您交流! ----------------------

0 0
原创粉丝点击