4.3常用对象API-集合框架(Collection,Map,泛型,集合工具类)

1.容器:数据封装(数组,StringBuilder,对象,集合)

数值很多,用数组存。
数组很多,用二维数组存。

数据很多,用对象存。
对象有很多,有集合存。

数组是固定长度,集合是可变长度。

容器

2.集合:
1.基本内容

1.集合类的由来:
    对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。
    就使用集合容器进行存储。
2.集合特点:
    1.用于存储对象的容器。
    2.集合的长度是可变的。
    3.集合中不可以存储基本数据类型值。
3.集合容器因为内部的数据结果不同,有多种具体容器。
不断向上抽取,就形成了集合框架。
4.集合框架的顶层是Collection根接口,包含共性内容:
集合在工具包java.util里
        1.添加
            boolean add(Object obj);
            boolean addAll(Collection coll);
        2.删除
            boolean remove(Object obj);
            boolean removeAll(Collection coll);  //将两个集合中的相同元素从调用removeAll的集合中删除
            void clear();  清空
        3.判断
            boolean contains(Object obj);   //集合是否包含对象
            boolean containsAll(Collection coll); //集合是否包含集合
            boolean isEmpty();  判断集合中是否有元素
        4.获取
            int size();
            Iterator iterator(); 取出元素的方式:迭代器。
            该对象必须依赖于具体容器,因为每一个容器的数据结构不同。
            所以该迭代器对象是在容器中进行内部实现的。
            对于容器使用者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可。
            也就是iterator()方法。

            Iterator接口就是对所有的Collection容器进行元素取出的公共接口。


        5.其他:
            boolean retainAll(Collection coll); //取两个集合的交集,和removeAll功能相反
            Object[] toArray(); 将集合转成数组

2.集合框架的构成图
集合框架的构成图

3.List集合接口:有序

特点:
1.存入和取出的顺序一致,元素都有索引
2.元素可重复

特有的常见方法:
1.添加:
    void add(index,element);
    void addAll(index, collection);
2.删除:
    Object remove(index);
3.修改:
    Object set(intdex,element);
4.获取:
    Object get(index);   //list特有的取出方式之一
    int indexOf(Object);
    int lastIndexOf(Object);
    List subList(from,to);

ListIterator接口:Iterator子接口,存在增删改查方法

解决在使用迭代器操作集合的同时,又在使用集合本身操作集合,而出现的异常。

List list = new ArrayList();
list.add("abc1");
list.add("abc2");

/*
Iterator it = list.lterator();
while(it.hasNext()){
    Object obj = it.next();
    if(obj.equals("abc2")){
        list.add("abc3");     //在迭代器过程中,不用使用集合操作元素,容易出现异常。
    }   
}*/

ListIterator it = list.listIterator(); 
        //它可以实现在迭代过程中完成对元素的增删改查。
        //注意只有list集合具备该迭代功能
while(it.hasNext()){
    Object obj = it.next();
    if(obj.equals("abc2")){
        it.add("abc3");  //正确处理
    }   
}

/*减少内存写法:
for(Iterator it = coll.iterator();it.hasNext();){
    System.out.println(it.next);
}
*/

List接口常用的实现类:Vector, ArrayList, LinkedList

1.Vector:   内部是数组数据结构,是同步的。(数组100%延长,基本不用了)增删,查询都很慢。
2.ArrayList:内部是数组数据结构,是不同步的。替代了Vector。数组50%延长。查询的速度快(空间是连续的)。增删慢(一动而动全身)

3.LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快,查询慢(空间不是连续的)。元素也是有编号的的,只是不连续。
LinkedList:

addFirst();
addLast();
jdk1.6
offerFirst();
offerLast();

getFirst();  //获取但不移除,如果链表为空,抛出NoSuchElementException
getLast();
jdk1.6
peekFirst(); //获取但不移除,如果链表为空,返回Null,可以做判断
peekLast();

removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException
removeLast();
jdk1.6
pollFirst(); //获取并移除,如果链表为空,返回Null,可以做判断
pollLast();

Vector接口中获取元素的方法 :
Enumeration elements();返回一个枚举类型对象。
API:
接口 Enumeration的功能与 Iterator 接口的功能是重复的。此外,Iterator 接口添加了一个可选的移除操作,并使用较短的方法名。新的实现应该优先考虑使用 Iterator 接口而不是 Enumeration 接口。

    Vector v = new Vector();
    v.addElement("abc1");
    v.addElement("abc1");

/*
    Enumeration en = v.elements();
    while(en.hasMoreElement()){
        System.out.println(en.nextElement());
    }*/


    Iterator it = it.iterator();
    while(it.hasNext()){
        System.out.println(it.next());
    }

LinkedList :

LinkeList link = new LinkeList();
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");

System.out.println(link.getFirst());  //获取第一个但不删除
System.out.println(link.removeFirst());  //获取第一个并删除

while(!link.isEmpty()){
    System.out.println(link.removeFirst());  //不用迭代器输出对象,但是会删除集合对象。
}


Iterator it = link.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}

//LinkedList集合练习题:

/*使用LinkedList模拟一个堆栈或者队列数据结构。

堆栈:先进后出 First In Last Out FIFO

队列:先进先出 First In First Out FIFO

应该描述这样一个容器,给使用者提供一个容器对象完成这两种结构中的一种。

*/
//工具类
class DuiLie{
private LinkedList link;

public DuiLie(){
    link = new LinkedList();
}

public void myAdd(Object obj){
    link.addLast(obj);
}

public Object myGet(){
    return link.removeFirst();
}

pubilc boolean isNull(){
    return link.isEmpty();
}
}

ArrayList集合存储自定义对象:

public class ArrayListTest {

    public static void main(String[] args) {

        ArrayList al = new ArrayList();
        al.add(new Person("list1",21));//public boolean add(Object obj),所以后面需要强转
        al.add(new Person("list2",22));
        al.add(new Person("list3",23));
        al.add(new Person("list4",24));

        Iterator it = al.iterator();
        while(it.hasNext()){
//          System.out.println(((Person)it.next()).getName()+ " : "+ ((Person) it.next()).getAge());
            Person p = (Person) it.next();    //强转
            System.out.println(p.getName() + " : " + p.getAge());
        }

//al.add(5);   //自动装箱

    }

}

//ArrayList集合练习题:
ArrayList集合在判断元素是否相同,使用的是元素的equals()方法,只需覆盖equals()方法即可。
而hashSet则需要覆盖hashCode()方法和equals()方法。

数据结构不一样,对元素的判断依据也不一样。

//定义功能去除ArrayList中的重复元素。

    //存储字符串的情况:
    public static  ArrayList getSingleElement(ArrayList al){
        //1.定义一个临时容器
        ArrayList temp = new ArrayList();

        //2.迭代al集合
        Iterator it = al.iterator();
        while(it.hasNext()){
            Object obj = (Object) it.next();

        //3.判断被迭代的元素是否在临时容器中存在
            if(!temp.contains(obj))
                temp.add(obj);
        }       
        return temp;
    }

    //存储自定义对象的情况:在自定义类中要重写equals()方法
    public static  ArrayList getSingleElement(ArrayList al){
        //1.定义一个临时容器
        ArrayList temp = new ArrayList();

        //2.迭代al集合
        Iterator it = al.iterator();
        while(it.hasNext()){
            Person p = (Person) it.next();

        //3.判断被迭代的元素是否在临时容器中存在
            if(!temp.contains(p))   //比较的原理是调用了object的equals方法。
                temp.add(p);
        }

        return temp;
    }

    public boolean equals(Object obj) {
        Person p = (Person) obj;
        return this.name.equals(p.name) && this.age == p.age;
    }

4.Set集合接口:不重复

特点:
1.元素不能重复
2.无序(有可能“有序”)

set接口中的方法和Collection一致。

|--HashSet:内部数据结构是哈希表(实际上是一个 HashMap 实例) ,是不同步的。
    是通过对象的hashcode和equals方法来判断对象唯一性。
    如果对象的hashcode值不同,那么不用判断equals方法,直接存储到哈希表中。
    如果对象的hashcode值相同,那么要再次判断对象的equals方法是否为true。
        如果为true,视为相同元素,不存。如果为false,视为不同元素,就进行存储。

    记住,如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。
    一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals方法,hashcode方法,建立对象判断是否相同的依据。

|--TreeSet:可以对set集合中的元素进行排序。是不同步的。
            判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。

        ①TreeSet对元素进行排序的方式一:
            让元素自身具备比较功能,元素需要实现comparable接口。覆盖compareTo方法。

        如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。
        ②可以使用TreeSet集合的第二种排序方式二:
            让集合自身具备比较功能。在集合创建对象时完成。定义一个类实现Comparator接口,覆盖compare方法。将该类的对象作为参数传递给TreeSet集合的构造函数。

TreeSet集合内部数据结构:二叉树(红黑树)(每次插入元素前都会默认记录折中元素的位置,采用二分查找提高效率)
TreeSet集合内部结构_二叉树

HashSet集合存储自定义对象:

public class HashSetTest {

    public static void main(String[] args) {

        HashSet hs = new HashSet();

        hs.add(new Person("list1",24));
        hs.add(new Person("list2",25));
        hs.add(new Person("list3",26));
        hs.add(new Person("list2",25));


        Iterator it = hs.iterator();

        while(it.hasNext()){
            Person p = (Person) it.next();
            System.out.println(p.getName()+" : "+ p.getAge());
        }
    }

}


public class Person {
    private String name;
    private int age;

    public Person() {
        super();

    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }


    @Override
    public int hashCode() {
//      System.out.println(this+ ".......hashcode");
        return name.hashCode() + age*38;  //减少哈希值相同概率
    }

    @Override
    public boolean equals(Object obj) {
//      System.out.println(this+ "....equals..." + obj);

        if(this == obj)
            return ture;
        if(obj instanceof Person)
            throw new ClassCastException("类型错误");
        Person p = (Person) obj;
        return this.name.equals(p.name) && this.age == p.age;
    }



    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public String toString()
    {
        return name +": "+age;
    }

}

LinkedHashSet:由哈希表和链表实现。有序,怎么存怎么取。(注意和排序区别)。
所以判断用什么集合,无关有无顺序。需要唯一性用set集合,不需要就用list集合。

HashSet hs = new LinkedHashSet();
hs.add("abc1");
hs.add("abc2");
hs.add("abc3");

Iterator it = hs.iterator();
while(it.hasNext()){
    System.out.println(it.next());    //顺序输出
}

TreeSet集合对元素进行排序的方式一:存储自定义对象时需要实现 Comparable接口,复写compareTo()方法。

public class TreeSetDemo {

    public static void main(String[] args) {
            TreeSet<Person> ts =new TreeSet<Person>();

            ts.add(new Person("zhangsan",28));
            ts.add(new Person("wangwu",21));
            ts.add(new Person("lisi",22));
            ts.add(new Person("zhaoliu",21));

            Iterator<Person> it = ts.iterator();
            while(it.hasNext()){
                Person p =it.next();
                System.out.println(p.getName()+":"+ p.getAge());
            }

//          demo1(ts);

    }

    public static void demo1(TreeSet ts) {
        ts.add("abc");    //字符串本身就重写了compareTo方法。
        ts.add("zaa");
        ts.add("aa");
        ts.add("nba");
        ts.add("dwe");

        Iterator it = ts.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

}


public class Person implements Comparable<Person>{   //实现Comparable接口
    private String name;
    private int age;

    public Person() {
        super();

    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person p) {    //重写compareTo方法。按年龄排序
    //  Person p = (Person)o;       //元素比较传入一个参数

        int temp =  this.age - p.age;
        return temp==0?this.name.compareTo(p.name):temp;
    }

}

比较器Comparator:更常用
(如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。)
TreeSet集合对元素进行排序的方式二:让集合自身具备比较功能。在集合创建对象时完成。
定义一个类实现Comparator接口,覆盖compare方法。将该类的对象作为参数传递给TreeSet集合的构造函数。

TreeSet有序化:如果compare返回值固定是正数,则会按存入顺序输出,如果是负数,则逆序输出。

public class TreeSetDemo {

    public static void main(String[] args) {
            TreeSet ts =new TreeSet(new ComparatorByName());    //使用比较器自定义排序功能

            ts.add(new Person("zhangsan",28));
            ts.add(new Person("wangwu",21));
            ts.add(new Person("lisi",22));
            ts.add(new Person("zhaoliu",21));

            Iterator it = ts.iterator();
            while(it.hasNext()){
                Person p =(Person) it.next();
                System.out.println(p.getName()+":"+ p.getAge());
            }       
    }

public class ComparatorByName implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {   //集合自身比较需要传2个参数
        Person p1 = (Person)o1;
        Person p2 = (Person)o2;

        int temp = p1.getName().compareTo(p2.getName());
        return temp ==0?p1.getAge() - p2.getAge():temp;
    }

}

3.Map集合:和Collection一样的顶层接口。

Map:一次添加一对元素。Collection一次添加一个元素。
    Map也称为双列集合,Collection称为单列集合。
    其实Map集合中存储的就是键值对。
    Map集合中必须保证 键 的唯一性。

常用方法:
1.添加
    value put(key, value); 返回前一个和key关联的值,如果没有返回null
2.删除
    void clear(); 清空map集合
    value remove(key); 根据指定的键删除这个键值对
3.判断
    boolean containKey(key);
    boolean containValue(value);
    boolean isEmpty();
4.获取
    value get(key);通过键获取值。如果没有该键返回null。
                    当然可以通过返回null,来判断是否包含指定键。
    int size(); 获取键值对的个数。

取出map中的所有元素:

①keySet()方法:返回所包含的键的set视图。返回set集合。

public class MapDemo {

    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<Integer,String>();
        method(map);
    }

    public static void method(Map<Integer,String> map){
        map.put(8, "wangwu");
        map.put(3, "lisan");
        map.put(2, "sunhao");
        map.put(6, "tiantian");

        //取出map中的所有元素
        //原理:通过keyset方法获取map中所有的键所在的set集合,再通过set的迭代器获取到每一个键,
        //再对每一个键通过map集合的get方法获取其对应的值即可

        Set<Integer> keySet = map.keySet();
        Iterator<Integer> it = keySet.iterator();
        while(it.hasNext()){
            Integer key = it.next();
            String value = map.get(key);
            System.out.println(key+":"+value);
        }

    }
}

keySet方法演示

②entrySet()方法:返回所包含的映射关系的set视图。返回set集合。

/*
    通过Map转换成set就可以迭代。
    通过entrySet方法 将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.Entry类型。
 */ 

        Set<Map.Entry<Integer, String>> entrySet = map.entrySet();

        Iterator<Map.Entry<Integer, String>> it = entrySet.iterator();
        while(it.hasNext()){
            Map.Entry<Integer, String> me = it.next();

            System.out.println(me.getKey()+":"+me.getValue());

        }

entrySet()演示
③values():返回此映射中包含的值的 Collection 视图。

Collection<String> values = map.values();

Iterator<String> it = values.iterator();
        while(it.hasNext()){            
            System.out.println(it.next());      
        }

Map集合的常见子类对象:1.0时单列集合就是Vector,双列集合就是Hashtable。

1.Hashtable:内部机构是哈希表,是同步的。不允许null作为键和值。
        类Properties:用来存储键值对型的配置文件的信息。可以和IO技术相结合。
2.HashMap:内部结构是哈希表,是不同步的。允许null作为键和值。
3.TreeMap:内部结构是二叉树,是不同步的。可以对Map集合中的键进行排序。

LinkedHashMap:由哈希表和链表实现。有序,怎么存怎么取。(注意和排序区别)。

Map集合练习:

/*
 * 练习:
 * "fdgavcbsacdfs"获取该字符串中吗,每一个字母出现的次数。
 * 要求打印的结果是:a(2)b(1)...
 * 
 * 对于结果的分析发现,字母和次数之间存在映射关系,而且这种关系很多。
 * 很多就需要存储,能存储映射关系的容器有数组和Map集合。
 * 
 * 关系中没有一方是有序编号,所以用Map集合。
 * 因为保证唯一性的一方具有顺序a,b...,所以使用TreeMap集合。
 * 
 *这个集合最终存储的是字母和次数的对应关系。
 *
 *1.因为操作的是字符串中的字母,所以先将字符串变成字符数组。
 *2.遍历字符数组,用每一个字母作为键去查Map集合这个表。
 *如果该字母键不存在,就将该字母作为键,1作为值存储到map集合中。
 *如果该字母键存在,就将该字母键的值取出并+1,再将该字母和+1后的值存储到map集合中。键相同值会覆盖。
 *3.遍历结束,map集合就记录了所有字母的次数。
 */

public class Test {

    public static void main(String[] args) {
            String str = "f   --2dgavcbsacdfs";
            String s = getCharCount(str);

            System.out.println(s);
         }

         public static String getCharCount(String str){
             //将字符串变成字符数组
             char[] chs = str.toCharArray();

             //定义map集合表
             Map<Character,Integer> map = new TreeMap<Character, Integer>();

             for (int i = 0; i < chs.length; i++) {
                if(!(chs[i]>='a' && chs[i] <='z' || chs[i]>='A' && chs[i] <='Z'))
                    continue;

                 //将数组中的字符作为键去查map表。
                 Integer value = map.get(chs[i]);

                 int count = 1;   //简化代码

                 //判断值是否为null
                 if(value!=null)
                 {
                     count = value + 1;
                 }
                 map.put(chs[i],count);
                /* 
                    if(value ==null){
                     map.put(chs[i],1);
                 }
                 else{
                     map.put(chs[i], value+1);
                 }*/

            }
             //return map.toString();
             return mapToString(map);
         }

        private static String mapToString(Map<Character, Integer> map) {

            StringBuilder sb = new StringBuilder();

            Iterator<Character> it = map.keySet().iterator();
            while(it.hasNext()){
                Character key = it.next();
                Integer value = map.get(key);

                sb.append(key +"("+ value +")");

            }

            return sb.toString();
        }

}


Map在有映射关系时,可以优先考虑。
在查表法中的应用较为多见。

4.泛型:jdk1.5出现的安全机制。编译时期的安全技术。

好处:
1.将运行时期的问题ClassCastException转到了编译时期。
2.避免了强制转换的麻烦。

<>:
什么时候用:当操作的引用数据类型不确定的时候,就是用<>。将操作的引用数据类型传入即可。
其实<>就是一个用于接收具体引用数据类型的参数范围。

在程序中,只要用到了带有<>的类或接口,就要明确传入的具体引用数据类型。

泛型的 擦除和补偿:

泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。

擦除:运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
    为什么擦除:为了兼容运行的类加载器。

补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者再强制转换。

泛型在集合中的应用:

public class TreeSetDemo {

    public static void main(String[] args) {
            TreeSet<Person> ts =new TreeSet<Person>();

            ts.add(new Person("zhangsan",28));
            ts.add(new Person("wangwu",21));
            ts.add(new Person("lisi",22));
            ts.add(new Person("zhaoliu",21));

            Iterator<Person> it = ts.iterator();
            while(it.hasNext()){
                Person p =it.next();
                System.out.println(p.getName()+":"+ p.getAge());
            }   
    }

public class Person implements Comparable<Person>{   //实现Comparable接口
    private String name;
    private int age;

    public Person() {
        super();

    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }


    public int compareTo(Person p) {    //重写compareTo方法。按年龄排序
    //  Person p = (Person)o;       //元素比较传入一个参数

        int temp =  this.age - p.age;
        return temp==0?this.name.compareTo(p.name):temp;
    }

}

泛型类:

/*
 *在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
 *泛型类,什么时候用:当类中的操作的引用数据类型不确定的时候,就使用泛型类表示。
 *
 */
public class Tool<Q>{
    private Q q;

    public Q getObject() {
        return q;
    }

    public void setObject(Q object) {
        this.q = object;
    }
}
/*public class Tool {
    private Object object;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }


}*/


public class GenericDefineDemo3 {

    public static void main(String[] args) {

        Tool<Student> tool = new Tool<Student>();  //使用泛型类

        tool.setObject(new Student());   //如果传入的对象不对,编译报错

        Student stu = tool.getObject();


/*      Tool tool = new Tool();

        tool.setObject(new Worker());

        Student stu = (Student)tool.getObject();*/
    }

}

泛型方法:<>放在修饰符的后面,返回值的前面。
一旦使用了泛型,只能使用Object的方法。

//将泛型定义在方法上

public <W> void show(W str){
        System.out.println("show:" + str);
    }

public <Q>void print(Q str){
        System.out.println("print:"+ str);
    }

/*
//当方法静态时,不能访问 类 上定义的泛型,
//如果静态方法使用泛型,只能将泛型定义在方法上。

public static void method(Q str){
        System.out.println("method:"+ str);
    }

//正确的写法:
public static<Y> void method(Y str){
        System.out.println("method:"+ str);
    }


*/


public static void main(String[] args) {

        Tool<String> tool = new Tool<String>();
        tool.show("abc");  //自动选择参数类型
        tool.print(new Integer(4)); //报错,print方法是根据Tool类的泛型来确定
    }

泛型接口:

interface Inter<T>{
    public void show(T t);
}

class InterImpl implements Inter<String>{
    public void show(String str){
        System.out.println("show :" + str);
    }
}

class InterImpl2 implements Inter<Q>{
    public void show(Q q){
        System.out.println("show :" + q);
    }
}



public static void main(String[] args){
    InterImpl in = new InterImpl();
    in.show("haha");

    InterImpl2<Integer> in2 = new InterImpl2<Integer>();
    in2.shoe(5);
}

泛型的高级应用:泛型的限定

泛型的通配符:?未知类型

public class GenericDemo4 {

    public static void main(String[] args) {

        ArrayList<String> al = new ArrayList<String>();
        al.add("abc");
        al.add("hehe");

        ArrayList<Integer> al2 = new ArrayList<Integer>();
        al2.add(55);
        al2.add(67);

        printCollection(al);
        printCollection(al2);

    }

    /**
     * 迭代并打印集合中的元素
     * @param al
     */
    //泛型的通配符?

    private static  void printCollection(Collection<?> al) {
        Iterator<?> it = al.iterator();

        while(it.hasNext()){
    //      T t = it.next();
            System.out.println(it.next().toString());
        }
    }

    /*
    private static <T> void printCollection(Collection<T> al) {
        Iterator<T> it = al.iterator();

        while(it.hasNext()){
            T t = it.next();
            System.out.println(t.toString());
        }
    }*/

}

泛型的限定:? ( 就是 ? extends Object)

/*
    ?extends E :接收E类型或者E的子类型对象。上限。
    ?super E: 接收E类型或者E类型的父类型。下限。
*/
private static  void printCollection(Collection<? extends Person> al) {
        Iterator<? extends Person> it = al.iterator();

        while(it.hasNext()){
    //      T t = it.next();
            Person p = it.next();
            System.out.println(p.toString());
        }
    }

泛型限定在集合中的应用:

上限的体现:Collection中的addAll()方法。
boolean addAll(Collection<? extends E> c) 
    将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 

原因:如果不限定,则所有的对象都可以添加,这时在取出的时候需要强制转换,容易出现问题。

一般在存储元素的时候,都使用上限。因为这样取出都是按照上限类型来运算的,不会出现类型安全隐患。

下限的体现:TreeSet中的构造方法:
TreeSet(Comparator<? super E> comparator) 
       构造一个新的空 TreeSet,它根据指定比较器进行排序。

通常对集合中的元素进行取出操作时,可以用下限。用得较少。


通配符?的体现:Collection中的containsAll()方法。
 boolean containsAll(Collection<?> c) 
       如果此 collection 包含指定 collection 中的所有元素,则返回 true。 

因为equals方法比较两边可以是任意对象。

5.集合查阅的技巧:

需要唯一么?
需要:Set
    需要指定顺序么?
        需要:TreeSet
        不需要:HashSet
            但是想要一个和存储一致的顺序:LinkedHashSet
不需要:List
    需要频繁增删么?
        需要:LinkedList
        不需要:ArrayList

如何记住每一个容器的结果和所属体系?
后缀名就是该集合所属的体系:
List:
    ArrayList
    LinkedList

Set:
    HashSet
    TreeSet

前缀名就是该集合的数据结构:
Array:就要想到数组,想到查询快,有角标
Link:就要想到链表,想到增删快,想到add/get/remove + first/last的方法
Hash:就要想到哈希表,想到唯一性,想到元素需要覆盖hashcode方法和equals方法。
tree:就要想到二叉树,想到排序,想到两个接口Comparable,Comparator。

通常这些常用的集合都是不同步的。

6.集合框架工具类:
Collections: 操作集合的工具类
1.排序:

/*
static <T extends Comparable<? super T>> void sort(List<T> list) 
        根据元素的自然顺序 对指定列表按升序进行排序。 
static <T> void sort(List<T> list, Comparator<? super T> c) 
        根据指定比较器产生的顺序对指定列表进行排序。 
static void swap(List<?> list, int i, int j) 
        在指定列表的指定位置处交换元素。
*/



public class CollectionsDemo {

    public static void main(String[] args) {

        /*
         * Collections:是集合框架的工具类。
         * 里面的方法都是静态的。
         */
        demo_1();
    }

    public static void demo_1(){
        List<String> list = new ArrayList<String>();

        list.add("abc");
        list.add("cba");
        list.add("aa");
        list.add("zzz");
        list.add("cba");  //有重复的,不能用TreeSet
        list.add("nbaa");
        System.out.println(list);

        //对list集合进行指定顺序的排序。

        Collections.sort(list);
        System.out.println(list);
        Collections.sort(list,new ComparatorByLength());
        System.out.println(list);


        //源码实现:
        mySort(list);
        System.out.println(list);
        mySort1(list,new ComparatorByLength());
        System.out.println(list);
    }

    public static <T> void mySort1(List<T> list, Comparator<? super T> comp){
        for (int i = 0; i < list.size()-1; i++) {
            for (int j = i+1; j < list.size(); j++) {
                if(comp.compare(list.get(i), list.get(j))>0){
//                  T temp = list.get(i);
//                  list.set(i, list.get(j));
//                  list.set(j, temp);

                    Collections.swap(list, i, j);
                }
            }
        }
    }

    public static<T extends Comparable<? super T>> void mySort(List<T > list){
        for (int i = 0; i < list.size()-1; i++) {
            for (int j = i+1; j < list.size(); j++) {
                if(list.get(i).compareTo(list.get(j))>0){
//                  T temp = list.get(i);
//                  list.set(i, list.get(j));
//                  list.set(j, temp);

                    Collections.swap(list, i, j);
                }
            }
        }
    }

}

2.折半,最值,逆序,替换,置换,集合和枚举的转换,非同步集合转同步集合。

折半: 先有序化,然后查找。根据有序化分为两种方式:
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 需要自己先排序
          使用二分搜索法搜索指定列表,以获得指定对象。 
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c) 
          使用二分搜索法搜索指定列表,以获得指定对象。 

最值:
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 不需要自己排序
          根据元素的自然顺序,返回给定 collection 的最大元素。 
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 
          根据指定比较器产生的顺序,返回给定 collection 的最大元素。 

逆序:
static <T> Comparator<T> reverseOrder() 
        返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。 
static <T> Comparator<T> reverseOrder(Comparator<T> cmp) 
        返回一个比较器,它强行逆转指定比较器的顺序。 
static void reverse(List<?> list) 
        反转指定List列表中元素的顺序。 

例子:TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()); //String自己实现了Comparable 接口
原理:TreeSet<String> ts = new TreeSet<String>(new Comparator<String>{
    public int compare(String o1,String o2){
        int temp = o2.compareTo(o2);
        return temp;
}
});


替换:
    static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 
          使用另一个值替换列表中出现的所有某一指定值。 
     static <T> void fill(List<? super T> list, T obj) 
          使用指定元素替换指定列表中的所有元素。 
置换:
     static void shuffle(List<?> list) 
          使用默认随机源对指定列表进行置换。 
     static void shuffle(List<?> list, Random rnd) 
          使用指定的随机源对指定列表进行置换。

集合和枚举的转换:
    static <T> ArrayList<T> list(Enumeration<T> e) 
          返回一个数组列表,它按返回顺序包含指定枚举返回的元素。 
    static <T> Enumeration<T> enumeration(Collection<T> c) 
          返回一个指定 collection 上的枚举。  

非同步集合转同步集合:
static <T> List<T> synchronizedList(List<T> list) 
          返回指定列表支持的同步(线程安全的)列表。 
static <T> Set<T> synchronizedSet(Set<T> s) 
          返回指定 set 支持的同步(线程安全的)set。 
static <T> Collection<T> synchronizedCollection(Collection<T> c) 
          返回指定 collection 支持的同步(线程安全的)collection。 
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 
          返回由指定映射支持的同步(线程安全的)映射。      

给非同步的集合加锁 原理:

List list = new ArrayList();   //非同步的
list = MyCollection.synList(list);   //返回一个同步的list

class MyCollection{
    public List synList(List list){
        return new MyList(list);
    }

 private class MyList implements List{
    private List list;
    private static final Object lock = new Object();
    MyList(List list){
        this.list = list;
    }

    public boolean add(Object obj){
        synchronized(lock){
            return list.add(obj);
        }
    }

    public boolean remove(Object obj){
        synchronized(lock){
            return list.remove(obj);
        }
    }
}
}

Arrays:操作数组的集合工具类

1.折半:包括byte,char,double,float,int,short,long,Object,T泛型数组。
static int binarySearch(byte[] a, byte key) 
          使用二分搜索法来搜索指定的 byte 型数组,以获得指定的值。 
static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key) 
          使用二分搜索法来搜索指定的 byte 型数组的范围,以获得指定的值。 
...
static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) 
          使用二分搜索法来搜索指定数组,以获得指定对象。 
static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) 
          使用二分搜索法来搜索指定数组的范围,以获得指定对象。        

2.复制指定数组:包括boolean,byte,char,double,float,int,short,long,T泛型数组。
static boolean[] copyOf(boolean[] original, int newLength) 
          复制指定的数组,截取或用 false 填充(如有必要),以使副本具有指定的长度。  

3.复制指定范围指定数组:包括boolean,byte,char,double,float,int,short,long,T泛型数组。
static boolean[] copyOfRange(boolean[] original, int from, int to) 
          将指定数组的指定范围复制到一个新数组。 

4.判断两个数组是否相等(是指内容全部相同):包括boolean,byte,char,double,float,int,short,long,Object数组。
static boolean equals(boolean[] a, boolean[] a2) 
        如果两个指定的 boolean 型数组彼此相等,则返回 true5.重置:包括boolean,byte,char,double,float,int,short,long,Object数组。
static void fill(boolean[] a, boolean val) 
         将指定的 boolean 值分配给指定 boolean 型数组的每个元素。
static void fill(boolean[] a, int fromIndex, int toIndex, boolean val) 
         将指定的 boolean 值分配给指定 boolean 型数组指定范围中的每个元素。    

6.哈希码:包括boolean,byte,char,double,float,int,short,long,Object数组。
static int hashCode(boolean[] a) 
          基于指定数组的内容返回哈希码。  

7.排序:包括boolean,byte,char,double,float,int,short,long,Object,T泛型数组。
static void sort(byte[] a) 
         对指定的 byte 型数组按数字升序进行排序。                         
static void sort(byte[] a, int fromIndex, int toIndex) 
          对指定 byte 型数组的指定范围按数字升序进行排序。
...
static <T> void sort(T[] a, Comparator<? super T> c) 
          根据指定比较器产生的顺序对指定对象数组进行排序。 
static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) 
          根据指定比较器产生的顺序对指定对象数组的指定范围进行排序。    

8.字符串表示:包括boolean,byte,char,double,float,int,short,long,Object数组。
static String toString(boolean[] a) 
          返回指定数组内容的字符串表示形式。
原理:
    public static void main(String[] args) {

        /*
         * Arrays:集合框架的工具类。里面的方法都是静态的。
         */
        int[] arr = {3,1,2,5,6,3};
        System.out.println(Arrays.toString(arr));
    }

    //toString的经典实现。
    public static String myToString(int[] a) {
        if (a == null)
            return "null";
        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {    //中间省略判断,提高了效率
            b.append(a[i]);
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    } 

9.其他:
static boolean deepEquals(Object[] a1, Object[] a2) 
          如果两个指定数组彼此是深层相等 的,则返回 true。除了比较数组的元素对象,还比较对象的内容。      
10.将数组转成List集合: Arrays-asList方法

static <T> List<T> asList(T... a) 
          返回一个受指定数组支持的固定大小的列表。 

原理: 
/*
    重点:List asList(数组)    将数组转成集合。

    好处:可以使用集合的方法操作数组中的元素。
    注意:
    ①数组的长度是固定的,所以对于集合的增删方法是不可以使用的。否则会发生java.lang.UnsupportedOperationException。

    ②如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行存储。
      如果数组中的元素是基本数据类型数值,那么会将该数组作为集合中的元素进行存储。
      int [] arr = {13,25,65};
      List<int[]> list = Arrays.asList(arr);

    原因:想知道数组是否包含某元素。
         想知道元素在数组中的位置。
    但是数组集合工具类中没有这些方法。
*/
public static void main(String[] args) {    

        String[] arr = {"abc","haha","xixi"};
        boolean b = myContains(arr, "haha");
        System.out.println(b);

        List<String> list = Arrays.asList(arr);//转换成列表
        boolean b1 = list.contains("haha");
        System.out.println(b1);
    // list.add("a");  //错误操作                   
    }

public static boolean myContains(String[] arr, String key){
        for (int i = 0; i < arr.length; i++) {
            if(arr[i].equals(key))
                return true;
        }
        return false;
    }
11.将集合转成数组: Collection接口-toArray方法
 Object[] toArray() 
       返回包含此 collection 中所有元素的数组。 
 T[] toArray(T[] a) 
       返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。 

原理:

/*
集合转换数组:
     使用Collection接口中的toArray方法。

好处:可以对集合中的元素操作的方法进行限定。不允许对其进行增删。

*/     

public static void main(String[] args) {

        List<String> list = new ArrayList<String>();
        list.add("abc1");
        list.add("abc2");
        list.add("abc3");

        /*
            Object[] toArray() 
                    返回包含此 collection 中所有元素的数组。 

         */
        Object[] arr1 = list.toArray();
        System.out.println(Arrays.toString(arr1));

        /*
            T[] toArray(T[] a) 
                    返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。

        注意:
            该方法需要传入一个指定类型的数组。
            如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同size的数组。
            如果长度大于集合的size,那么该方法会使用指定的数组,存储集合中的元素,其他位置默认null。

            建议,最后长度就指定为,集合的size.

          */
        String[] arr = list.toArray(new String[list.size()]);
        System.out.println(Arrays.toString(arr));
    }

7.JDK1.5特性

①.JDK1.5给Collection接口找了个父接口Iterable:

实现这个接口允许对象成为 “foreach” 语句的目标。

foreach语句:底层还是实现的迭代。
格式:
for(类型 变量: Collection集合|数组)
{
}

举例:
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
/*
Iterator<String> it = list.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
*/
foreach(String s:list){
    System.out.println(s);
}
传统for和高级for的区别:

foreach局限性:
必须有遍历的目标(数组或Collection单列集合),一般只用于遍历,不会对元素进行过多的操作。是用来简化书写的。

传统for循环相对foreach的优势:
可以定义控制循环的条件和增量。

使用场景:
对数组的遍历如果仅仅是获取数组中的元素,可以使用foerach。
如果要对数组的角标进行操作建议使用传统for
对于map集合,不能直接使用foreach遍历。但是可以将map转换成单列的set,就可以用了。

例子:
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("小米", 2000);
        map.put("苹果", 5000);
        map.put("三星", 4000);

//entrySet:     
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //map变成set集合
        for (Map.Entry<String, Integer> entry : entrySet) {                 //foreach遍历
            System.out.println(entry.getKey() + ":" +entry.getValue());
        }

//keySet:
for(String key : map.keySet()){
    Integer value = map.get(key);
    System.out.println(key + ": " + value);
}

//传统迭代:     
        Iterator<Map.Entry<String,Integer>> it= map.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<String, Integer> me = it.next();
            System.out.println(me.getKey() + ":" + me.getValue());
        }
    }

2.函数可变参数:int …
内部执行的动作:创建并将参数封装成数组。

其实就是一个数组,但是接收的是数组的元素。
自动将这些元素封装成数组。简化了调用者的书写。

注意事项:可变参数类型必须定义在参数列表的结尾处。

//求多个正数的和

public static void main(String[] args){
    int sum = add(2,5,6,6);
    System.out.println("sum= " + sum);

    int sum1 = add(2,5,6);
    System.out.println("sum1= " + sum1);

}

public static int add(int... arr){  //int... 和int[]等效,简化书写。调用传参的时候不需要建立数组,直接传值。
    int sum = 0;
    for(int i:arr)
        sum+=i;
    return sum;

}

3.静态导入:导入类的静态成员,方便程序简化书写。
因为静态成员不需要对象,直接使用,所以需要在程序加载前进行静态导入。

import static java.util.Collections.sort;
import static java.util.Collections.max;
或者:
import static java.util.Collections.*;

    sort(list);
    max(list);

import static java.lang.System.*;

    out.println("123");  //out是静态的,println不是静态的。out对应一个对象。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值