[关闭]
@liayun 2016-06-01T23:09:09.000000Z 字数 14131 阅读 1884

Java集合框架——Collection

java基础


集合类

集合框架的构成及分类

java中集合类的关系图

为什么会出现这么多的容器呢?
答:因为每一个容器对数据的存储方式都有不同。这个存储方式称之为:数据结构。

集合框架中的常用接口

Collection接口

集合中的最大接口——Collection,可以通过查看JDK帮助文档,了解Collection接口中的最共性的方法。
创建一个集合容器,使用Collection接口的子类——ArrayList

  1. ArrayList al = new ArrayList();
  1. al.add("java01"); // add(Object obj);
  2. al.add("java02");
  3. al.add("java03");
  4. al.add("java04");

注意:
1. add方法的参数类型是Object,以便于接受任意类型。
2. 集合中存储的都是对象的引用(地址)。

  1. al.remove("java02");
  1. al.clear();
  1. System.out.println("java03是否存在:"+al.contains("java03"));
  2. System.out.println("集合是否为空?"+al.isEmpty());
  1. public static void method() {
  2. ArrayList al1 = new ArrayList();
  3. al1.add("java01");
  4. al1.add("java02");
  5. al1.add("java03");
  6. al1.add("java04");
  7. ArrayList al2 = new ArrayList();
  8. al2.add("java03");
  9. al2.add("java04");
  10. al2.add("java05");
  11. al2.add("java06");
  12. // al1.retainAll(al2); // 取交集,al1中只会保留和al2中相同的元素
  13. al1.removeAll(al2); // al1中移除al1和al2的交集
  14. System.out.println("al1:"+al1);
  15. System.out.println("al2:"+al2);
  16. }

迭代器(Iterator)

现重点描述迭代器(Iterator)。
什么是迭代器呢?
答:其实就是集合的取出元素的方式。
Iterator的初步解释:就是把取出方式定义在了集合的内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类。而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都有共性内容——判断和取出,那么可以将这些共性抽取,那么这些内部类都符合一个规则,该规则是Iterator
如何获取集合的取出对象呢?
答:通过一个对外提供的方法:iterator()
因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
例,

  1. public static void sop(Object obj) {
  2. System.out.println(obj);
  3. }
  4. public static void method_get() {
  5. ArrayList al = new ArrayList();
  6. al.add("java01");
  7. al.add("java02");
  8. al.add("java03");
  9. al.add("java04");
  10. /*
  11. Iterator it = al.iterator(); // 获取迭代器,用于取出集合中的元素
  12. while(it.hasNext()) {
  13. sop(it.next());
  14. }
  15. */
  16. // 开发时,这样写
  17. for (Iterator it = al.iterator(); it.hasNext(); ) {
  18. sop(it.next());
  19. }
  20. }

List(列表)

Collection接口有两个子接口:List(列表)Set(集)

List特有方法:凡是可以操作角标的方法都是该体系特有的方法。

例,

  1. ArrayList al = new ArrayList();
  2. // 添加元素
  3. al.add("java01");
  4. al.add("java02");
  5. al.add("java03");
  1. al.add(1, "java09");
  1. al.remove(2);
  1. al.set(2, "java007");
  1. System.out.println("get(1):"+al.get(1));
  1. for (int x = 0; x < al.size(); x++) {
  2. System.out.println("al("+x+")="+al.get(x));
  3. }
  1. Iterator it = al.iterator();
  2. while(it.hasNext()) {
  3. System.out.println("next:"+it.next());
  4. }
  1. System.out.println("index = "+al.indexOf("java02"));
  1. List sub = al.subList(1, 3);
  2. System.out.println("sub="+sub);

现在我们来思考这样一个问题:在迭代过程中,准备添加或者删除元素。

  1. ArrayList al = new ArrayList();
  2. al.add("java01");
  3. al.add("java02");
  4. al.add("java03");
  5. // 在迭代过程中,准备添加或者删除元素
  6. Iterator it = al.iterator();
  7. while(it.hasNext()) {
  8. Object obj = it.next();
  9. if(obj.equals("java02"))
  10. al.add("java008"); // 出现并发修改异常
  11. System.out.println("obj="+obj);
  12. }

在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生并发修改异常(ConcurrentModificationException)。
迭代器还有另一个方法:remove(),我们可以试着用一下:

  1. Iterator it = al.iterator();
  2. while(it.hasNext()) {
  3. Object obj = it.next();
  4. if(obj.equals("java02"))
  5. it.remove(); // 将java02的引用从集合中删除了,但java02的引用还被obj使用
  6. System.out.println("obj="+obj); // 注意:虽然移除掉了"java02",但还能输出
  7. }

所以,在迭代时,只能用迭代器的方法操作元素,可是Iterator的方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口listIterator——List集合特有的迭代器(listIterator)。该接口只能通过List集合的listIterator()获取。
例,

  1. public static void main(String[] args) {
  2. // 演示列表迭代器
  3. ArrayList al = new ArrayList();
  4. // 添加元素
  5. al.add("java01");
  6. al.add("java02");
  7. al.add("java03");
  8. System.out.println(al);
  9. ListIterator li = al.listIterator();
  10. while(li.hasNext()) {
  11. Object obj = li.next();
  12. if(obj.equals("java02"))
  13. // li.add("java009");
  14. li.set("java006");
  15. }
  16. while(li.hasPrevious()) {
  17. System.out.println("pre:"+li.previous());
  18. }
  19. System.out.println(al);
  20. }

Vector类以及枚举(Enumeration)

枚举就是Vector特有的取出方式,通过API帮助文档可发现枚举和迭代器很像。
其实枚举和迭代是一样的。因为枚举的名称以及方法的名称都过长,所以被迭代器取代了,枚举郁郁而终了。
迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举),例:

  1. import java.util.*;
  2. class VectorDemo {
  3. public static void main(String[] args) {
  4. Vector v = new Vector();
  5. v.add("java01");
  6. v.add("java02");
  7. v.add("java03");
  8. v.add("java04");
  9. Enumeration en = v.elements();
  10. while(en.hasMoreElements()) {
  11. System.out.println(en.nextElement());
  12. }
  13. }
  14. }

LinkedList

LinkedList特有方法:

在JDK1.6出现了替代方法:

例,

  1. import java.util.*;
  2. class LinkedListDemo {
  3. public static void main(String[] args) {
  4. LinkedList link = new LinkedList();
  5. link.addLast("java01");
  6. link.addLast("java02");
  7. link.addLast("java03");
  8. link.addLast("java04");
  9. // sop(link.getFirst());
  10. // sop(link.getFirst());
  11. // sop(link.getLast());
  12. // sop(link.removeFirst());
  13. // sop(link.removeFirst());
  14. // sop("size="+link.size());
  15. while(!link.isEmpty()) {
  16. sop(link.removeLast());
  17. }
  18. }
  19. public static void sop(Object obj) {
  20. System.out.println(obj);
  21. }
  22. }

面试题:使用LinkedList模拟一个堆栈或者队列数据结构(必须会)。
解:
分析:
堆栈:先进后出——如同一个杯子。
队列:先进先出(First In First Out)FIFO——如同一个水管。

  1. import java.util.*;
  2. // 基于LinkedList来描述
  3. class DuiLie {
  4. private LinkedList link;
  5. DuiLie() {
  6. link = new LinkedList();
  7. }
  8. public void myAdd(Object obj) {
  9. link.addFirst(obj);
  10. }
  11. public Object myGet() {
  12. return link.removeLast(); // 模拟队列
  13. // return link.removeFirst(); // 模拟堆栈
  14. }
  15. public boolean isNull() {
  16. return link.isEmpty();
  17. }
  18. }
  19. class LinkedListTest {
  20. public static void main(String[] args) {
  21. DuiLie dl = new DuiLie();
  22. dl.myAdd("java01");
  23. dl.myAdd("java02");
  24. dl.myAdd("java03");
  25. dl.myAdd("java04");
  26. while(!dl.isNull())
  27. System.out.println(dl.myGet());
  28. }
  29. }

ArrayList

练习一:去除ArrayList集合中的重复元素(集合中元素是字符串)。

  1. import java.util.*;
  2. class ArrayListTest {
  3. public static void sop(Object obj) {
  4. System.out.println(obj);
  5. }
  6. public static void main(String[] args) {
  7. ArrayList al = new ArrayList();
  8. al.add("java01");
  9. al.add("java02");
  10. al.add("java01");
  11. al.add("java02");
  12. al.add("java01");
  13. al.add("java03");
  14. sop(al);
  15. al = singleElement(al);
  16. sop(al);
  17. }
  18. public static ArrayList singleElement(ArrayList al) {
  19. // 定义一个临时容器
  20. ArrayList newAl = new ArrayList();
  21. Iterator it = al.iterator();
  22. while(it.hasNext()) {
  23. Object obj = it.next();
  24. if(!newAl.contains(obj)) // 判断临时容器中是否包含传递进来的容器中的元素
  25. newAl.add(obj);
  26. }
  27. return newAl;
  28. }
  29. }

练习二:将自定义对象作为元素存到ArrayList集合中,并去除重复元素。比如:存人对象。同姓名同年龄,视为同一个人,为重复元素。
解:
思路:

  1. 对人描述,将数据封装到人对象。
  2. 定义容器,将人存入。
  3. 取出。
  1. import java.util.*;
  2. class Person {
  3. private String name;
  4. private int age;
  5. Person(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public boolean equals(Object obj) {
  16. if(!(obj instanceof Person))
  17. return false;
  18. Person p = (Person)obj;
  19. System.out.println(this.name+"..."+p.name);
  20. return this.name.equals(p.name) && this.age == p.age;
  21. }
  22. }
  23. class ArrayListTest2 {
  24. public static void sop(Object obj) {
  25. System.out.println(obj);
  26. }
  27. public static void main(String[] args) {
  28. ArrayList al = new ArrayList();
  29. al.add(new Person("lisi01", 30)); // add(Object obj); Object obj = new Person("lisi01", 30);
  30. al.add(new Person("lisi02", 32));
  31. al.add(new Person("lisi02", 32));
  32. al.add(new Person("lisi04", 35));
  33. al.add(new Person("lisi03", 33));
  34. al.add(new Person("lisi04", 35));
  35. al = singleElement(al);
  36. Iterator it = al.iterator();
  37. while(it.hasNext()) {
  38. Person p = (Person)it.next();
  39. sop(p.getName()+"::"+p.getAge());
  40. }
  41. }
  42. public static ArrayList singleElement(ArrayList al) {
  43. // 定义一个临时容器
  44. ArrayList newAl = new ArrayList();
  45. Iterator it = al.iterator();
  46. while(it.hasNext()) {
  47. Object obj = it.next();
  48. if(!newAl.contains(obj)) // contains底层用的是equals()
  49. newAl.add(obj);
  50. }
  51. return newAl;
  52. }
  53. }

通过以上示例,说明contains底层用的是equals()。
ArrayList去除重复元素的示意图:
ArrayList去除重复元素的示意图
再来看以下代码:

  1. import java.util.*;
  2. class Person {
  3. private String name;
  4. private int age;
  5. Person(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public boolean equals(Object obj) {
  16. if(!(obj instanceof Person))
  17. return false;
  18. Person p = (Person)obj;
  19. System.out.println(this.name+"..."+p.name);
  20. return this.name.equals(p.name) && this.age == p.age;
  21. }
  22. }
  23. class ArrayListTest2 {
  24. public static void sop(Object obj) {
  25. System.out.println(obj);
  26. }
  27. public static void main(String[] args) {
  28. ArrayList al = new ArrayList();
  29. al.add(new Person("lisi01", 30)); // add(Object obj); Object obj = new Person("lisi01", 30);
  30. al.add(new Person("lisi02", 32));
  31. al.add(new Person("lisi04", 35));
  32. al.add(new Person("lisi03", 33));
  33. sop("remove 03:"+al.remove(new Person("lisi03", 33))); // remove底层用的也是equals()
  34. Iterator it = al.iterator();
  35. while(it.hasNext()) {
  36. Person p = (Person)it.next();
  37. sop(p.getName()+"::"+p.getAge());
  38. }
  39. }
  40. }

通过以上示例,说明remove底层用的也是equals()。
结论:List集合判断元素是否相同,依据的是元素的equals方法。

Set(集)

通过查看JDK文档,发现Set集合的功能和Collection的是一致的。

HashSet

Hashset是如何保证元素的唯一性的呢?
答:是通过元素的两个方法,hashCode()equals()来完成的。如果元素的HashCode值相同,才会判断equals()是否为true。如果元素的HashCode值不同,不会调用equals()
例,

  1. import java.util.*;
  2. class HashSetDemo {
  3. public static void sop(Object obj) {
  4. System.out.println(obj);
  5. }
  6. public static void main(String[] args) {
  7. HashSet hs = new HashSet();
  8. sop(hs.add("java01")); // true
  9. sop(hs.add("java01")); // false
  10. hs.add("java02");
  11. hs.add("java03");
  12. hs.add("java03");
  13. hs.add("java04");
  14. Iterator it = hs.iterator();
  15. while(it.hasNext()) {
  16. sop(it.next());
  17. }
  18. }
  19. }

练习:往hashSet集合中存入自定义对象(人)。姓名和年龄相同为同一个人,重复元素。
解:

  1. import java.util.*;
  2. class HashSetTest {
  3. public static void sop(Object obj) {
  4. System.out.println(obj);
  5. }
  6. public static void main(String[] args) {
  7. HashSet hs = new HashSet();
  8. hs.add(new Person("a1", 11));
  9. hs.add(new Person("a2", 12));
  10. hs.add(new Person("a3", 13));
  11. hs.add(new Person("a2", 12));
  12. hs.add(new Person("a4", 14));
  13. // sop(hs.contains(new Person("a1", 11)));
  14. // hs.remove(new Person("a4", 13));
  15. Iterator it = hs.iterator();
  16. while(it.hasNext()) {
  17. Person p = (Person)it.next();
  18. sop(p.getName()+"::"+p.getAge());
  19. }
  20. }
  21. }
  22. class Person {
  23. private String name;
  24. private int age;
  25. Person(String name, int age) {
  26. this.name = name;
  27. this.age = age;
  28. }
  29. public String getName() {
  30. return name;
  31. }
  32. public int getAge() {
  33. return age;
  34. }
  35. public int hashCode() {
  36. System.out.println(this.name+".....hashCode");
  37. return name.hashCode() + age * 39; // 39是一个任意的数字
  38. }
  39. public boolean equals(Object obj) {
  40. if(!(obj instanceof Person))
  41. return false;
  42. Person p = (Person)obj;
  43. System.out.println(this.name+"...equals..."+p.name);
  44. return this.name.equals(p.name) && this.age == p.age;
  45. }
  46. }

Hashset保证元素的唯一性示意图:
Hashset保证元素的唯一性示意图
注意:对于判断元素是否存在,以及删除、添加等操作,依赖的方法是元素的hashCode()equals()方法。

TreeSet

例,需求:往TreeSet集合中存储自定义对象学生。想按照学生的年龄进行排序。
解:

TreeSet排序的第一种方式:

  1. import java.util.*;
  2. class Student implements Comparable { // 该接口强制让学生具备比较性
  3. private String name;
  4. private int age;
  5. Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public int compareTo(Object obj) {
  16. if(!(obj instanceof Student))
  17. throw new RuntimeException("不是学生对象");
  18. Student s = (Student)obj;
  19. System.out.println(this.name+"...compareTo..."+s.name);
  20. if(this.age > s.age)
  21. return 1;
  22. if(this.age == s.age) { // 当年龄相同时,再判断一下姓名的自然排序
  23. return this.name.compareTo(s.name);
  24. }
  25. return -1;
  26. }
  27. }
  28. class TreeSetDemo {
  29. public static void main(String[] args) {
  30. TreeSet ts = new TreeSet();
  31. ts.add(new Student("lisi02", 22));
  32. ts.add(new Student("lisi007", 20));
  33. ts.add(new Student("lisi09", 19));
  34. ts.add(new Student("lisi08", 19));
  35. Iterator it = ts.iterator();
  36. while(it.hasNext()) {
  37. Student stu = (Student)it.next();
  38. System.out.println(stu.getName()+"..."+stu.getAge());
  39. }
  40. }
  41. }

二叉树原理:
二叉树原理
记住,排序时,当主要条件相同时,一定判断一下次要条件。
思考:需求元素变为怎么存进去的就怎么取出来的,怎么办?
这时可依据二叉树原理来实现,只要让compareTo()方法返回正数即可,比如:

  1. import java.util.*;
  2. class Student implements Comparable { // 该接口强制让学生具备比较性
  3. private String name;
  4. private int age;
  5. Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public int compareTo(Object obj) {
  16. return 1;
  17. }
  18. }
  19. class TreeSetDemo {
  20. public static void main(String[] args) {
  21. TreeSet ts = new TreeSet();
  22. ts.add(new Student("lisi02", 22));
  23. ts.add(new Student("lisi007", 20));
  24. ts.add(new Student("lisi09", 19));
  25. ts.add(new Student("lisi08", 19));
  26. Iterator it = ts.iterator();
  27. while(it.hasNext()) {
  28. Student stu = (Student)it.next();
  29. System.out.println(stu.getName()+"..."+stu.getAge());
  30. }
  31. }
  32. }

TreeSet排序的第二种方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的,这时就需要让容器自身具备比较性。定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。当两种排序都存在时,以比较器为主。
定义一个类实现Comparator接口,覆盖compare()方法。

  1. import java.util.*;
  2. class Student implements Comparable { // 该接口强制让学生具备比较性
  3. private String name;
  4. private int age;
  5. Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public int getAge() {
  13. return age;
  14. }
  15. public int compareTo(Object obj) {
  16. if(!(obj instanceof Student))
  17. throw new RuntimeException("不是学生对象");
  18. Student s = (Student)obj;
  19. // System.out.println(this.name+"...compareTo..."+s.name);
  20. if(this.age > s.age)
  21. return 1;
  22. if(this.age == s.age) {
  23. return this.name.compareTo(s.name);
  24. }
  25. return -1;
  26. }
  27. }
  28. class TreeSetDemo2 {
  29. public static void main(String[] args) {
  30. TreeSet ts = new TreeSet(new MyCompare()); // 此时学生对象自己具备的比较方式不是我们所需要的
  31. ts.add(new Student("lisi02", 22));
  32. ts.add(new Student("lisi02", 21));
  33. ts.add(new Student("lisi007", 20));
  34. ts.add(new Student("lisi09", 19));
  35. ts.add(new Student("lisi06", 18));
  36. ts.add(new Student("lisi06", 18));
  37. ts.add(new Student("lisi007", 29));
  38. Iterator it = ts.iterator();
  39. while(it.hasNext()) {
  40. Student stu = (Student)it.next();
  41. System.out.println(stu.getName()+"..."+stu.getAge());
  42. }
  43. }
  44. }
  45. class MyCompare implements Comparator {
  46. public int compare(Object o1, Object o2) {
  47. Student s1 = (Student)o1;
  48. Student s2 = (Student)o2;
  49. int num = s1.getName().compareTo(s2.getName());
  50. if(num == 0) { // 当姓名的自然排序相同时,再判断一下年龄的自然排序
  51. return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge())); // 简写
  52. // 以下这种方法很繁琐
  53. /*
  54. if(s1.getAge() > s2.getAge())
  55. return 1;
  56. if(s1.getAge() == s2.getAge())
  57. return 0;
  58. return -1;
  59. */
  60. }
  61. return num;
  62. }
  63. }

练习:按照字符串长度排序。
解:
字符串本身具备比较性,但是它的比较方式不是所需要的,这时就只能使用比较器。

  1. import java.util.*;
  2. class TreeSetTest {
  3. public static void main(String[] args) {
  4. TreeSet ts = new TreeSet(new Comparator());
  5. ts.add("abcd");
  6. ts.add("cc");
  7. ts.add("cba");
  8. ts.add("aaa");
  9. ts.add("z");
  10. ts.add("hahaha");
  11. Iterator it = ts.iterator();
  12. while(it.hasNext()) {
  13. System.out.println(it.next());
  14. }
  15. }
  16. java
  17. ----------
  18. }
  19. class StrLengthComparator implements Comparator {
  20. public int compare(Object o1, Object o2) {
  21. String s1 = (String)o1;
  22. String s2 = (String)o2;
  23. // 以下这种方式繁琐
  24. /*
  25. if(s1.length() > s2.length())
  26. return 1;
  27. if(s1.length() == s2.length())
  28. return 0;
  29. return -1;
  30. */
  31. int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); // 简写
  32. if(num == 0) // 当字符串长度相同时,再按照其自然顺序排序
  33. return s1.compareTo(s2);
  34. return num;
  35. }
  36. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注