Java-集合框架

概述

  • 集合类的由来:
    • 对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。就使用集合容器进行存储。
  • 集合特点:
    • 用于存储对象的容器。
    • 集合的长度是可变的。
    • 集合中不可以存储基本数据类型值。

体系

  • 集合容器因为内部的数据结构不同,有多种具体容器。不断的向上抽取,就形成了集合框架。
  • 框架的顶层就是Collection接口:
    • List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
    • Set:元素不能重复,无序。

功能(Collection接口)

  • 添加

    1
    2
    boolean add(E e);
    boolean addAll(Collection coll);
  • 判断

    1
    2
    3
    boolean remove(object obj):
    boolean removeAll(Collection coll);
    void clear();
  • 删除

    1
    2
    3
    boolean contains(object obj):
    boolean containsAll(Colllection coll);
    boolean isEmpty();//判断集合中是否有元素。
  • 获取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int size():
    Iterator iterator():取出元素的方式:迭代器。
    该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。
    所以该迭代器对象是在容器中进行内部实现的。
    对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,
    也就是iterator方法。

    Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
    其实就是抓娃娃游戏机中的夹子!
  • 其他

    1
    2
    boolean retainAll(Collection coll);//取交集。
    Object[] toArray();//将集合转成数组。

方法演示

  • 展示一下如何使用这些方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    public class CollectionDemo {

    /**
    * @param args
    */
    public static void main(String[] args) {
    Collection coll = new ArrayList();

    // show(coll);

    Collection c1 = new ArrayList();
    Collection c2 = new ArrayList();
    show(c1,c2);

    }

    public static void show(Collection coll){

    //1,添加元素。add.
    coll.add("abc1");
    coll.add("abc2");
    coll.add("abc3");
    System.out.println(coll);

    //2,删除元素。remove
    coll.remove("abc2");//会改变集合的长度

    //清空集合.
    //coll.clear();

    System.out.println(coll.contains("abc3"));
    System.out.println(coll);

    }

    public static void show(Collection c1,Collection c2){

    //给c1添加元素。
    c1.add("abc1");
    c1.add("abc2");
    c1.add("abc3");
    c1.add("abc4");

    //给c2添加元素。
    c2.add("abc1");
    c2.add("abc2");
    c2.add("abc3");
    c2.add("abc4");
    c2.add("abc5");

    System.out.println("c1:"+c1);
    System.out.println("c2:"+c2);

    //演示addAll
    // c1.addAll(c2);//将c2中的元素添加到c1中。

    //演示removeAll
    // boolean b = c1.removeAll(c2);//将两个集合中的相同元素从调用removeAll的集合中删除。
    // System.out.println("removeAll:"+b);

    //演示containsAll
    // boolean b = c1.containsAll(c2);
    // System.out.println("containsAll:"+b);

    //演示retainAll
    boolean b = c1.retainAll(c2);//取交集,保留和指定的集合相同的元素,而删除不同的元素。
    //和removeAll功能相反 。
    System.out.println("retainAll:"+b);
    System.out.println("c1:"+c1);
    }

    }

迭代器

  • 演示迭代器方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public static void main(String[] args) {

    Collection coll = new ArrayList();
    coll.add("abc1");
    coll.add("abc2");
    coll.add("abc3");
    coll.add("abc4");

    // System.out.println(coll);

    // 使用了Collection中的iterator()方法。 调用集合中的迭代器方法,是为了获取集合中的迭代器对象。
    // Iterator it = coll.iterator();
    // while(it.hasNext()){
    // System.out.println(it.next());
    // }

    //这样写也可以
    for(Iterator it = coll.iterator(); it.hasNext(); ){
    System.out.println(it.next());
    }

    }
    }
  • 迭代器图解

List

  • List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。

  • 特有的常见方法:有一个共性特点就是都可以操作角标。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    1,添加
    void add(index,element);
    void add(index,collection);

    2,删除;
    Object remove(index):

    3,修改:
    Object set(index,element);

    4,获取:
    Object get(index);
    int indexOf(object);
    int lastIndexOf(object);
    List subList(from,to);
  • list集合是可以完成对元素的增删改查。

ListIterator

  • 在迭代器过程中,不要使用集合操作元素,容易出现异常。

  • 否则会报错(java.util.ConcurrentModificationException)

  • 可以使用Iterator接口的子接口ListIterator来完成在迭代中对元素进行更多的操作。

  • 它可以实现在迭代过程中完成对元素的增删改查。

  • 注意:只有list集合具备该迭代功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public static void main(String[] args) {

    List list = new ArrayList();

    list.add("abc1");
    list.add("abc2");
    list.add("abc3");

    System.out.println("list:"+list);
    ListIterator it = list.listIterator();

    while(it.hasNext()){

    Object obj = it.next();
    if(obj.equals("abc2")){
    it.set("abc9");
    }
    }
    System.out.println("hasNext:"+it.hasNext());
    //判断是否有上一个数据
    System.out.println("hasPrevious:"+it.hasPrevious());
    //反向迭代数据
    while(it.hasPrevious()){
    System.out.println("previous:"+it.previous());
    }
    System.out.println("list:"+list);
    }

List常用子类特点

  • Vector:内部是数组数据结构,是同步的。增删,查询都很慢!
  • ArrayList:内部是数组数据结构,是不同步的。替代了Vector。查询的速度快。
  • LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。
  • Vector 的Enumeration方法和迭代器功能相同,但名字过长

LinkedList

  • jdk1.6的方法更新

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    getFirst();.//获取但不移除,如果链表为空,抛出NoSuchElementException.
    getLast();
    JDK1.6
    peekFirst();//获取但不移除,如果链表为空,返回null.
    peekLast():

    removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException.
    removeLast();
    JDK1.6
    pollFirst();//获取并移除,如果链表为空,返回null.
    pollLast();

存储自定义对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ArrayListTest {

public static void main(String[] args) {
Person p1 = new Person("lisi1",21);

ArrayList al = new ArrayList();
al.add(p1);
al.add(new Person("lisi2",22));
al.add(new Person("lisi3",23));
al.add(new Person("lisi4",24));

Iterator it = al.iterator();
while(it.hasNext()){
//在这里进行向下转型,以使用子类特有方法
Person p = (Person) it.next();
System.out.println(p.getName()+"--"+p.getAge());
}

}


}

链表练习

  • 用链表简单模拟一个堆栈及队列的输入输出过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import java.util.Iterator;
    import java.util.LinkedList;

    public class LinkedListDemo {

    public static void main(String[] args) {

    LinkedList link = new LinkedList();

    link.addFirst("abc1");
    link.addFirst("abc2");
    link.addFirst("abc3");
    link.addFirst("abc4");
    // System.out.println(link);
    // System.out.println(link.getFirst());//获取第一个但不删除。
    // System.out.println(link.getFirst());

    // System.out.println(link.removeFirst());//获取元素但是会删除。
    // System.out.println(link.removeFirst());

    while(!link.isEmpty()){
    System.out.println(link.removeLast());
    }


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

    }

Set

  • Set:元素不可以重复,是无序。Set接口中的方法和Collection一致。

HashSet

  • HashSet: 内部数据结构是哈希表 ,是不同步的。

  • 如何保证该集合的元素唯一性呢?

    • 是通过对象的hashCode和equals方法来完成对象唯一性的!
    • 如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。
    • 如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
    • 如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。
  • 记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。建立对象判断是否相同的依据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //Person类有name,年龄
    //重写hashCode,equals
    public int hashCode() {
    return name.hashCode()+age*27;
    return 100;
    }
    public boolean equals(Object obj) {
    //判断是否是同一个对象
    if(this == obj)
    return true;
    //判断是否属于Person类
    if(!(obj instanceof Person))
    throw new ClassCastException("类型错误");
    Person p = (Person)obj;
    return this.name.equals(p.name) && this.age == p.age;
    }

练习

  • 将Person类中去除相同元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //前提:Person中
    public static ArrayList getSingleElement(ArrayList al) {

    //1,定义一个临时容器。
    ArrayList temp = new ArrayList();

    //2,迭代al集合。
    Iterator it = al.iterator();

    while(it.hasNext()){
    Object obj = it.next();

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

    return temp;
    }

LinkedHashSet

  • 与HashSet区别就是HashSet无序,LinkedHashSet有序,

TreeSet

  • TreeSet可以对Set集合中的元素进行排序。是不同步的。

  • 判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。

  • TreeSet对元素进行排序的方式一:

  • 让元素自身具备比较功能,就需要实现Comparable接口。覆盖compareTo方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //Person中的方法
    public int compareTo(Object o) {
    //向下转型前最好进行健壮性增强
    Person p = (Person)o;

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

    /*
    if(this.age>p.age)
    return 1;
    if(this.age<p.age)
    return -1;
    else{
    return this.name.compareTo(p.name);
    }
    */

    }
  • 如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?

  • 可以使用TreeSet集合第二种排序方式二:

  • 让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。

  • 将该类对象作为参数传递给TreeSet集合的构造函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 创建了一个根据Person类的name进行排序的比较器。
    */
    public class ComparatorByName implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {

    Person p1 = (Person)o1;
    Person p2 = (Person)o2;

    int temp = p1.getName().compareTo(p2.getName());
    return temp==0?p1.getAge()-p2.getAge(): temp;
    //return 1; 这样写的话,注销前面的就意味着按输入顺序来
    }
    }
  • 内部实现用的二叉树,打个比方,返回正数为右子树,负数为左子树

泛型

  • jdk1.5出现的安全机制。
  • 好处:
    • 1、将运行时期的问题ClassCastException转到了编译时期。
    • 2、避免了强制转换的麻烦。
  • <>:什么时候用?
    • 当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型传入即可.
    • 其实<>就是一个用于接收具体引用数据类型的参数范围。
  • 在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型 。
  • 泛型技术是给编译器使用的技术,用于编译时期。确保了类型的安全。

擦除补偿

  • 运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
  • 为什么擦除呢?
    • 因为为了兼容运行的类加载器。
  • 泛型的补偿:
    • 在运行时,通过获取元素的类型进行转换动作。不用使用者在强制转换了。

泛型类

  • 在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。

  • 泛型类。什么时候用?

    • 当类中的操作的引用数据类型不确定的时候,就使用泛型来表示。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //主类调用此类时,需为其赋上数据类型
    public class Tool<QQ>{
    private QQ q;

    public QQ getObject() {
    return q;
    }

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

泛型方法

  • 泛型方法的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Tool<QQ>{
    private QQ q;

    public QQ getObject() {
    return q;
    }

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


    /**
    * 将泛型定义在方法上。
    * @param str
    */
    public <W> void show(W str){
    System.out.println("show : "+str.toString());
    }
    public void print(QQ str){
    System.out.println("print : "+str);
    }

    /**
    * 当方法静态时,不能访问类上定义的泛型。如果静态方法使用泛型,
    * 只能将泛型定义在方法上。
    * @param obj
    */
    public static <Y> void method(Y obj){
    System.out.println("method:"+obj);
    }
    }

通配符

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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(5);
    al2.add(67);

    printCollection(al);
    printCollection(al2);
    }
    /**
    * 迭代并打印集合中元素。
    * @param al
    */
    public static void printCollection(Collection<?> al) {
    Iterator<?> it = al.iterator();
    while(it.hasNext()){
    System.out.println(it.next().toString());
    }

    }

泛型的限定

  • 上限 ? extends E

    • 接收E类型或者E的子类型对象
    • 一般存储对象的时候用。比如 添加元素 addAll.
  • 下限 ? super E

    • 接收E类型或者E的父类型对象。
    • 一般取出对象的时候用。比如比较器。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static void printCollection(Collection<? extends Person> al) {

    Iterator<? extends Person> it = al.iterator();

    while(it.hasNext()){
    Person p = it.next();
    System.out.println(p.getName()+":"+p.getAge());
    }
    }
  • 什么时候用上限?

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

    • 通常对集合中的元素进行取出操作时,可以使用下限。如TreeSet的比较器

集合选择技巧

需要唯一吗??

  • 需要:Set
    • 需要制定顺序:
      • 需要: TreeSet
      • 不需要:HashSet
      • 但是想要一个和存储一致的顺序(有序):LinkedHashSet
  • 不需要:List
    • 需要频繁增删吗?
      • 需要:LinkedList
      • 不需要:ArrayList

如何记录各容器的结构和所属体系呢??

  • List
    • ArrayList
    • LinkedList
  • Set
    • HashSet
    • TreeSet
  • 后缀名就是该集合所属的体系前缀名就是该集合的数据结构
    看到array:就要想到数组,就要想到查询快,有角标.
  • 看到Link:就要想到链表,就要想到增删快,就要想要 add get remove方法及头插尾插法
  • 看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashCode方法和equals方法。
  • 看到tree:就要想到二叉树,就要想要排序,就要想到两个接口Comparable,Comparator 。
  • 而且通常这些常用的集合容器都是不同步的。

Map

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

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1,添加。
value put(key,value):返回前一个和key关联的值,如果没有返回null.

2,删除。
void clear():清空map集合。
value remove(key):根据指定的key删除这个键值对。

3,判断。
boolean containsKey(key):
boolean containsValue(value):
boolean isEmpty();

4,获取。
value get(key):通过键获取值,如果没有该键返回null
当然可以通过返回null,来判断是否包含指定键。
int size(): 获取键值对的个数。

重点方法

  • 取出map中所有元素
1
2
3
4
5
6
7
8
9
10
11
12
13
//取出map中的所有元素。
//原理,通过keySet方法获取map中所有的键所在的Set集合,在通过Set的迭代器获取到每一个键,
//在对每一个键通过map集合的get方法获取其对应的值即可。

public static void keySetDemo(Map<Integer,String> map){
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);
}

  • 取出map中所有元素–>第二种方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* 通过Map转成set就可以迭代。
* 找到了另一个方法。entrySet。
* 该方法将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.Entry类型(结婚证)
*/
public static void keySetDemo(Map<Integer,String> map){
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();
Integer key = me.getKey();
String value = me.getValue();
System.out.println(key+"::::"+value);
}

常用子类

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

练习一

  • 将学生对象和学生的归属地通过键与值存储到map集合中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void hashMapMethod(){
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put(new Student("lisi",38),"北京");
hm.put(new Student("zhaoliu",24),"上海");
hm.put(new Student("xiaoqiang",31),"沈阳");
hm.put(new Student("wangcai",28),"大连");
//需要对student类进行hashCode、equals方法的重写
//否则相同的student也会被传进去
hm.put(new Student("zhaoliu",24),"铁岭");

//Set<Student> keySet = hm.keySet();
//Iterator<Student> it = keySet.iterator();

Iterator<Student> it = hm.keySet().iterator();

while(it.hasNext()){
Student key = it.next();
String value = hm.get(key);
System.out.println(key.getName()+":"+key.getAge()+"---"+value);
}
}
  • 如果需要进行排序的话,使用TreeMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void treeMapDemo() {

TreeMap<Student,String> tm = new TreeMap<Student,String>(new ComparatorByName());

tm.put(new Student("lisi",38),"北京");
tm.put(new Student("zhaoliu",24),"上海");
tm.put(new Student("xiaoqiang",31),"沈阳");
tm.put(new Student("wangcai",28),"大连");
tm.put(new Student("zhaoliu",24),"铁岭");


Iterator<Map.Entry<Student, String>> it = tm.entrySet().iterator();

while(it.hasNext()){
Map.Entry<Student,String> me = it.next();
Student key = me.getKey();
String value = me.getValue();

System.out.println(key.getName()+":"+key.getAge()+"---"+value);
}

}
  • 如果需要按输入顺序存储的话,LinkedHashMap

练习二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

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

public class MapTest {

public static void main(String[] args) {
String str = "fdg+avAdc bs5dDa9c-dfs";
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'))
// if(!(Character.toLowerCase(chs[i])>='a' && Character.toLowerCase(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);

}

return mapToString(map);
}

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

StringBuilder sb = new StringBuilder();
//取出map中所有元素,更改格式存储
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();
}

}

工具类

Collections

  • Collections是集合框架的工具类, 里面的方法都是静态的。
  • 对list集合进行指定顺序的排列
1
2
3
4
5
//因为这个排序方法是基于比较器Comparable的,所以使用泛型
//这样继承了Comparable的类及其父类都可以使用该方法
public static <T extends Comparable<? super T>> void Sort(List<T> list){
//代码
}
  • 折半查找 binarySearch
  • 最值 max
  • 逆序
  • 替换 replace
  • 全部替换 fill
  • 将list随机洗牌 shuffle

给非同步集合加锁

  • 同步方法怎么实现的那?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MyCollections{

public static 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);
}
}

}
}
  • 那么该如何调用这个方法那?
1
2
3
List list = new ArrayList();//非同步的。

list = MyCollections.synList(list);//返回一个同步的list.

Arrays

toString

  • toString 经典实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//toString的经典实现。
public static String myToString(int[] a){
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(", ");
}
}

asList

  • asList 数组转 List 集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static void demo_1() {
/*
* 重点:List asList(数组)将数组转成集合。
*
* 好处:其实可以使用集合的方法操作数组中的元素。
*
* 注意:数组的长度是固定的,所以对于集合的增删方法是不可以使用的
* 否则会发生UnsupportedOperationException
*
*/
String[] arr = {"abc","haha","xixi"};

boolean b = myContains(arr, "xixi");
System.out.println("contains:"+b);

List<String> list = Arrays.asList(arr);
boolean b1 = list.contains("xixi");
System.out.println("list contaisn:="+b1);
//list.add("hiahia");//UnsupportedOperationException

System.out.println(list);
}

public static void demo_2() {

/*
* 如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行集合存储。
*
* 如果数组中的元素是基本类型数值,那么会将该数组作为集合中的元素进行存储。
*
*/
int[] arr = {31,11,51,61};

List<int[]> list = Arrays.asList(arr);

System.out.println(list);
}
  • toArray 集合转数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   /*
* 集合转成数组呢?
*
* 使用的就是Collection接口中的toArray方法。
*
* 集合转成数组:可以对集合中的元素操作的方法进行限定。不允许对其进行增删。
*/

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


/*
* toArray方法需要传入一个指定类型的数组。
* 长度该如何定义呢?
* 如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同size的数组。
* 如果长度大于集合的size,那么该方法就会使用指定的数组,存储集合中的元素,其他位置默认为null。
*
* 所以建议,最后长度就指定为,集合的size。
*/
String[] arr = list.toArray(new String[list.size()]);

System.out.println(Arrays.toString(arr));

JDK5 特性

ForEach

概述

1
2
3
4
5
6
foreach语句:
格式:
for(类型 变量 :Collection集合|数组)
{

}
  • 代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args){		
List<String> list =new ArrayList<String>();

list.add("abc1");
list.add("abc2");
list.add("abc3");

for(String s : list){ //简化书写。
System.out.println(s);
}


int[] arr = {3,1,5,7,4};

for(int i : arr){
System.out.println(i);
}

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

传统for、高级for区别

  • 传统for可以完成对语句执行很多次,因为可以定义控制循环的增量和条件。
  • 高级for是一种简化形式。
    • 它必须有被遍历的目标。该目标要是数组,要么是Collection单列集合。
  • 对数数组的遍历如果仅仅是获取数组中的元素,可以使用高级for。
  • 如果要对数组的角标进行操作建议使用传统for。

注意事项

  • 可以使用高级for遍历map集合吗?
  • 不能直接用,但是可以将map转成单列的set,就可以用了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Map<Integer,String> map = new HashMap<Integer,String>();

map.put(3,"zhagsan");
map.put(1,"wangyi");
map.put(7,"wagnwu");
map.put(4,"zhagsansan");


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

for(Map.Entry<Integer,String> me : map.entrySet()){
Integer key = me.getKey();
String value = me.getValue();

System.out.println(key+":"+value);
}

函数可变参数

  • 函数的可变参数。
    • 其实就是一个数组,但是接收的是数组的元素。
    • 自动将这些元素封装成数组。简化了调用者的书写。
    • 注意:可变参数类型,必须定义在参数列表的结尾。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) {

int sum = newAdd(5,1,4,7,3);
System.out.println("sum="+sum);
int sum1 = newAdd(5,1,2,7,3,9,8,7,6);
System.out.println("sum1="+sum1);

}

public static int newAdd(int... arr){

int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum+=arr[i];
}
return sum;
}
注意!!!!
public static int newAdd1(int... arr,int a){
会报错,因为arr吃掉了所有值
}
public static int newAdd2(int a,int... arr){
这样就没事了
}

静态导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.util.Collections.*;//静态导入,其实导入的是类中的静态成员。
//import static java.util.Collections.max;//静态导入,其实导入的是类中的静态成员。

import static java.lang.System.*;

public class StaticImportDemo {

/**
* @param args
* 注意:如果两个类中有相同名字的方法,并且都会被使用
* 一定不能静态导入,否则程序会不知道用哪个方法
*/
public static void main(String[] args) {

List<String> list = new ArrayList<String>();

list.add("abc3");
list.add("abc7");
list.add("abc1");

out.println(list);

//collection.sort(list);
sort(list); //静态导入后可以简写

String max = max(list);
System.out.println("max="+max);

}

}