[关闭]
@hainingwyx 2017-06-04T13:39:24.000000Z 字数 28616 阅读 1324

Java集合

Java


集合概述

Java集合可以分为Set、List、Map、Queue四种体系。Set是无序集合、List是有序集合、Queue是队列实现,Map保存映射关系。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口。

集合类:为了保存数量不确定的数据以及保存具有映射关系的数据。集合里只能保存对象,不能保存基本类型。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有集合类都位于java.util包下。

派生接口:Collection和Map

Collection和Iterator接口

Collection是List、Set、Queue接口的父接口。

  1. import java.util.*;
  2. public class CollectionTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. Collection c = new ArrayList();
  7. // 添加元素
  8. c.add("孙悟空");
  9. // 虽然集合里不能放基本类型的值,但Java支持自动装箱
  10. c.add(6);
  11. System.out.println("c集合的元素个数为:" + c.size()); // 输出2
  12. // 删除指定元素
  13. c.remove(6);
  14. System.out.println("c集合的元素个数为:" + c.size()); // 输出1
  15. // 判断是否包含指定字符串
  16. System.out.println("c集合的是否包含\"孙悟空\"字符串:"
  17. + c.contains("孙悟空")); // 输出true
  18. c.add("轻量级Java EE企业应用实战");
  19. System.out.println("c集合的元素:" + c);
  20. Collection books = new HashSet();
  21. books.add("轻量级Java EE企业应用实战");
  22. books.add("疯狂Java讲义");
  23. System.out.println("c集合是否完全包含books集合?"
  24. + c.containsAll(books)); // 输出false
  25. // 用c集合减去books集合里的元素
  26. c.removeAll(books);
  27. System.out.println("c集合的元素:" + c);
  28. // 删除c集合里所有元素
  29. c.clear();
  30. System.out.println("c集合的元素:" + c);
  31. // 控制books集合里只剩下c集合里也包含的元素
  32. books.retainAll(c);
  33. System.out.println("books集合的元素:" + books);
  34. }
  35. }
  1. import java.util.*;
  2. public class ForeachTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建集合、添加元素的代码与前一个程序相同
  7. Collection books = new HashSet();
  8. books.add(new String("轻量级Java EE企业应用实战"));
  9. books.add(new String("疯狂Java讲义"));
  10. books.add(new String("疯狂Android讲义"));
  11. for (Object obj : books)
  12. {
  13. // 此处的book变量也不是集合元素本身
  14. String book = (String)obj;
  15. System.out.println(book);
  16. if (book.equals("疯狂Android讲义"))
  17. {
  18. // 下面代码会引发ConcurrentModificationException异常
  19. books.remove(book); //①
  20. }
  21. }
  22. System.out.println(books);
  23. }
  24. }

Iterator也是Java集合框架的成员,主要用于遍历,Iterable接口是Collection接口的父接口。

  1. import java.util.*;
  2. public class CollectionEach
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建一个集合
  7. Collection books = new HashSet();
  8. books.add("轻量级Java EE企业应用实战");
  9. books.add("疯狂Java讲义");
  10. books.add("疯狂Android讲义");
  11. // 调用forEach()方法遍历集合,Collection集合直接调用父接口的默认方法
  12. // foreach 方法所需参数类型是一个函数型接口
  13. books.forEach(obj -> System.out.println("迭代集合元素:" + obj));
  14. }
  15. }
  1. import java.util.*;
  2. public class IteratorTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建集合、添加元素的代码与前一个程序相同
  7. Collection books = new HashSet();
  8. books.add("轻量级Java EE企业应用实战");
  9. books.add("疯狂Java讲义");
  10. books.add("疯狂Android讲义");
  11. // 获取books集合对应的迭代器
  12. Iterator it = books.iterator();
  13. while(it.hasNext())
  14. {
  15. // it.next()方法返回的数据类型是Object类型,因此需要强制类型转换
  16. String book = (String)it.next();
  17. System.out.println(book);
  18. if (book.equals("疯狂Java讲义"))
  19. {
  20. // 从集合中删除上一次next方法返回的元素
  21. it.remove();
  22. }
  23. // 对book变量赋值,不会改变集合元素本身
  24. book = "测试字符串";
  25. }
  26. System.out.println(books);
  27. }
  28. }

当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的remove方法删除上一次返回的集合元素才可以,否则引发java.util.ConcurrentModificationException异常

  1. import java.util.*;
  2. public class IteratorErrorTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建集合、添加元素的代码与前一个程序相同
  7. Collection books = new HashSet();
  8. books.add("轻量级Java EE企业应用实战");
  9. books.add("疯狂Java讲义");
  10. books.add("疯狂Android讲义");
  11. // 获取books集合对应的迭代器
  12. Iterator it = books.iterator();
  13. while(it.hasNext())
  14. {
  15. String book = (String)it.next();
  16. System.out.println(book);
  17. if (book.equals("疯狂Android讲义"))
  18. {
  19. // 使用Iterator迭代过程中,不可修改集合元素,下面代码引发异常
  20. books.remove(book);
  21. }
  22. }
  23. }
  24. }

迭代时检测到集合被修改将会引发异常,只有删除集合的某个特定元素才不会引发异常。

lambda表达式遍历Iterator
java8提供的forEachRemaining参数同样也是函数式接口。

  1. import java.util.*;
  2. public class IteratorEach
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建集合、添加元素的代码与前一个程序相同
  7. Collection books = new HashSet();
  8. books.add("轻量级Java EE企业应用实战");
  9. books.add("疯狂Java讲义");
  10. books.add("疯狂Android讲义");
  11. // 获取books集合对应的迭代器
  12. Iterator it = books.iterator();
  13. // 使用Lambda表达式(目标类型是Comsumer)来遍历集合元素
  14. it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));
  15. }
  16. }

使用foreach循环遍历集合元素
同样当使用foreach迭代访问集合元素时,该集合不能被改变。

  1. import java.util.*;
  2. public class ForeachTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建集合、添加元素的代码与前一个程序相同
  7. Collection books = new HashSet();
  8. books.add(new String("轻量级Java EE企业应用实战"));
  9. books.add(new String("疯狂Java讲义"));
  10. books.add(new String("疯狂Android讲义"));
  11. for (Object obj : books)
  12. {
  13. // 此处的book变量也不是集合元素本身
  14. String book = (String)obj;
  15. System.out.println(book);
  16. if (book.equals("疯狂Android讲义"))
  17. {
  18. // 下面代码会引发ConcurrentModificationException异常
  19. books.remove(book);
  20. }
  21. }
  22. System.out.println(books);
  23. }
  24. }

使用Predicate操作集合
Java8新增了一个removeIf(Predicate filter)方法,该方法将删除符合filter条件的所有元素。Predicate也是函数式接口,因此可以使用Lambda表达式作为参数。

  1. import java.util.*;
  2. import java.util.function.*;
  3. public class PredicateTest
  4. {
  5. public static void main(String[] args)
  6. {
  7. // 创建一个集合
  8. Collection books = new HashSet();
  9. books.add(new String("轻量级Java EE企业应用实战"));
  10. books.add(new String("疯狂Java讲义"));
  11. books.add(new String("疯狂iOS讲义"));
  12. books.add(new String("疯狂Ajax讲义"));
  13. books.add(new String("疯狂Android讲义"));
  14. // 使用Lambda表达式(目标类型是Predicate)过滤集合
  15. books.removeIf(ele -> ((String)ele).length() < 10);
  16. System.out.println(books);
  17. }
  18. }

使用Predicate可以简化集合的运算。callAll方法只统计满足Predicate条件的图书。

  1. import java.util.*;
  2. import java.util.function.*;
  3. public class PredicateTest2
  4. {
  5. public static void main(String[] args)
  6. {
  7. // 创建books集合、为books集合添加元素的代码与前一个程序相同。
  8. Collection books = new HashSet();
  9. books.add(new String("轻量级Java EE企业应用实战"));
  10. books.add(new String("疯狂Java讲义"));
  11. books.add(new String("疯狂iOS讲义"));
  12. books.add(new String("疯狂Ajax讲义"));
  13. books.add(new String("疯狂Android讲义"));
  14. // 统计书名包含“疯狂”子串的图书数量
  15. System.out.println(calAll(books , ele->((String)ele).contains("疯狂")));
  16. // 统计书名包含“Java”子串的图书数量
  17. System.out.println(calAll(books , ele->((String)ele).contains("Java")));
  18. // 统计书名字符串长度大于10的图书数量
  19. System.out.println(calAll(books , ele->((String)ele).length() > 10));
  20. }
  21. public static int calAll(Collection books , Predicate p)
  22. {
  23. int total = 0;
  24. for (Object obj : books)
  25. {
  26. // 使用Predicate的test()方法判断该对象是否满足Predicate指定的条件
  27. if (p.test(obj))
  28. {
  29. total ++;
  30. }
  31. }
  32. return total;
  33. }
  34. }

Java 8新增的Stream操作
Java 8还新增了Stream、IntStream、LongStream、DoubleStream等流式API。
独立使用Stream的步骤如下:

  1. 使用Stream或XxxStream的builder()类方法创建该Stream对应Builder。
  2. 重复调用Builder的add()方法向该流中添加多个元素。
  3. 调用Builder的build()方法获取对应的Stream。
  4. 调用Stream的聚集方法。
  1. import java.util.stream.*;
  2. public class IntStreamTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. IntStream is = IntStream.builder()
  7. .add(20)
  8. .add(13)
  9. .add(-2)
  10. .add(18)
  11. .build();
  12. // 下面调用聚集方法的代码每次只能执行一个
  13. System.out.println("is所有元素的最大值:" + is.max().getAsInt());
  14. System.out.println("is所有元素的最小值:" + is.min().getAsInt());
  15. System.out.println("is所有元素的总和:" + is.sum());
  16. System.out.println("is所有元素的总数:" + is.count());
  17. System.out.println("is所有元素的平均值:" + is.average());
  18. System.out.println("is所有元素的平方是否都大于20:"
  19. + is.allMatch(ele -> ele * ele > 20));
  20. System.out.println("is是否包含任何元素的平方大于20:"
  21. + is.anyMatch(ele -> ele * ele > 20));
  22. // 将is映射成一个新Stream,新Stream的每个元素是原Stream元素的2倍+1
  23. IntStream newIs = is.map(ele -> ele * 2 + 1);
  24. // 使用方法引用的方式来遍历集合元素
  25. newIs.forEach(System.out::println); // 输出41 27 -3 37
  26. }
  27. }

Collection接口提供了一个stream()默认方法,该方法可返回该集合对应的流,接下来即可通过流API来操作集合元素。由于Stream可以对集合元素进行整体的聚集操作,因此Stream极大了丰富了集合的功能。

  1. import java.util.*;
  2. import java.util.function.*;
  3. public class CollectionStream
  4. {
  5. public static void main(String[] args)
  6. {
  7. // 创建books集合、为books集合添加元素的代码与8.2.5小节的程序相同。
  8. Collection books = new HashSet();
  9. books.add(new String("轻量级Java EE企业应用实战"));
  10. books.add(new String("疯狂Java讲义"));
  11. books.add(new String("疯狂iOS讲义"));
  12. books.add(new String("疯狂Ajax讲义"));
  13. books.add(new String("疯狂Android讲义"));
  14. // 统计书名包含“疯狂”子串的图书数量
  15. System.out.println(books.stream()
  16. .filter(ele->((String)ele).contains("疯狂"))
  17. .count()); // 输出4
  18. // 统计书名包含“Java”子串的图书数量
  19. System.out.println(books.stream()
  20. .filter(ele->((String)ele).contains("Java") )
  21. .count()); // 输出2
  22. // 统计书名字符串长度大于10的图书数量
  23. System.out.println(books.stream()
  24. .filter(ele->((String)ele).length() > 10)
  25. .count()); // 输出2
  26. // 先调用Collection对象的stream()方法将集合转换为Stream,
  27. // 再调用Stream的mapToInt()方法获取原有的Stream对应的IntStream
  28. books.stream().mapToInt(ele -> ((String)ele).length())
  29. // 调用forEach()方法遍历IntStream中每个元素
  30. .forEach(System.out::println);// 输出8 11 16 7 8
  31. }
  32. }

Set集合

Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,添加操作失败,add方法返回false,且新元素不会被加入。

HashSet类是Set接口的典型实现。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值来决定该对象在HashSet中存储位置。如果有两个元素通过equals方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同位置,也就可以添加成功。

特点:无序、非同步、可以包含null。
HashSet判断元素相等:1.equals方法比较相等;2.hashCode()方法返回值相等。所以如果重写equals方法,需要重写hashCode方法。

  1. import java.util.*;
  2. // 类A的equals方法总是返回true,但没有重写其hashCode()方法
  3. class A
  4. {
  5. public boolean equals(Object obj)
  6. {
  7. return true;
  8. }
  9. }
  10. // 类B的hashCode()方法总是返回1,但没有重写其equals()方法
  11. class B
  12. {
  13. public int hashCode()
  14. {
  15. return 1;
  16. }
  17. }
  18. // 类C的hashCode()方法总是返回2,且重写其equals()方法总是返回true
  19. class C
  20. {
  21. public int hashCode()
  22. {
  23. return 2;
  24. }
  25. public boolean equals(Object obj)
  26. {
  27. return true;
  28. }
  29. }
  30. public class HashSetTest
  31. {
  32. public static void main(String[] args)
  33. {
  34. HashSet books = new HashSet();
  35. // 分别向books集合中添加两个A对象,两个B对象,两个C对象
  36. books.add(new A());
  37. books.add(new A());
  38. books.add(new B());
  39. books.add(new B());
  40. books.add(new C());
  41. books.add(new C());
  42. System.out.println(books);//输出2A,2B,1C
  43. }
  44. }

重写hashCode的基本规则:

程序把可变对象添加到HashSet中之后,尽量不要修改参与运算的hashCode()、equals()的实例变量,否则会导致HashSet无法正确操作这些集合元素。因为对象的Hashcode改变了,实际存储的位置并没有变化,无法对其进行准确的索引。

  1. import java.util.*;
  2. class R
  3. {
  4. int count;
  5. public R(int count)
  6. {
  7. this.count = count;
  8. }
  9. public String toString()
  10. {
  11. return "R[count:" + count + "]";
  12. }
  13. public boolean equals(Object obj)
  14. {
  15. if(this == obj)
  16. return true;
  17. if (obj != null && obj.getClass() == R.class)
  18. {
  19. R r = (R)obj;
  20. return this.count == r.count;
  21. }
  22. return false;
  23. }
  24. public int hashCode()
  25. {
  26. return this.count;
  27. }
  28. }
  29. public class HashSetTest2
  30. {
  31. public static void main(String[] args)
  32. {
  33. HashSet hs = new HashSet();
  34. hs.add(new R(5));
  35. hs.add(new R(-3));
  36. hs.add(new R(9));
  37. hs.add(new R(-2));
  38. // 打印HashSet集合,集合元素没有重复
  39. System.out.println(hs);
  40. // 取出第一个元素
  41. Iterator it = hs.iterator();
  42. R first = (R)it.next();
  43. // 为第一个元素的count实例变量赋值
  44. first.count = -3;
  45. // 再次输出HashSet集合,集合元素有重复元素
  46. System.out.println(hs);
  47. // 删除count为-3的R对象
  48. hs.remove(new R(-3));
  49. // 可以看到被删除了一个R元素
  50. System.out.println(hs);
  51. System.out.println("hs是否包含count为-3的R对象?"
  52. + hs.contains(new R(-3))); // 输出false
  53. System.out.println("hs是否包含count为-2的R对象?"
  54. + hs.contains(new R(-2))); // 输出false
  55. }
  56. }

LinkedHashSet
LinkedHashSet根据元素的hashCode来决定元素的存储位置,但同时使用链表维护元素的次序。当遍历LinkedHashSet集合里的元素时,将会按照元素的添加顺序来访问集合里的元素。因为需要维护元素的插入顺序,性能略低于HashSet的性能,但在迭代访问Set里的全部元素时有很好的性能。

  1. import java.util.*;
  2. public class LinkedHashSetTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. LinkedHashSet books = new LinkedHashSet();
  7. books.add("疯狂Java讲义");
  8. books.add("轻量级Java EE企业应用实战");
  9. System.out.println(books);//与添加顺序一致
  10. // 删除 疯狂Java讲义
  11. books.remove("疯狂Java讲义");
  12. // 重新添加 疯狂Java讲义
  13. books.add("疯狂Java讲义");
  14. System.out.println(books);
  15. }
  16. }

TreeSet
TreeSet是SortedSet接口的实现类,可以确保集合元素处于排序状态。

  1. import java.util.*;
  2. public class TreeSetTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. TreeSet nums = new TreeSet();
  7. // 向TreeSet中添加四个Integer对象
  8. nums.add(5);
  9. nums.add(2);
  10. nums.add(10);
  11. nums.add(-9);
  12. // 输出集合元素,看到集合元素已经处于排序状态
  13. System.out.println(nums);
  14. // 输出集合里的第一个元素
  15. System.out.println(nums.first()); // 输出-9
  16. // 输出集合里的最后一个元素
  17. System.out.println(nums.last()); // 输出10
  18. // 返回小于4的子集,不包含4
  19. System.out.println(nums.headSet(4)); // 输出[-9, 2]
  20. // 返回大于5的子集,如果Set中包含5,子集中还包含5
  21. System.out.println(nums.tailSet(5)); // 输出 [5, 10]
  22. // 返回大于等于-3,小于4的子集。
  23. System.out.println(nums.subSet(-3 , 4)); // 输出[2]
  24. }
  25. }

TreeSet采用红黑树的数据结构对元素进行排序。TreeSet支持两种排序方法:自然排序和定制排序。
1. 自然排序
TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按照升序排列。如果试图把一个对象添加到TreeSet时,则该对象必须实现Compareable接口的compareTo(Object obj)方法,否则抛出异常。

  1. import java.util.*;
  2. class Err{}
  3. public class TreeSetErrorTest
  4. {
  5. public static void main(String[] args)
  6. {
  7. TreeSet ts = new TreeSet();
  8. // 向TreeSet集合中添加两个Err对象
  9. ts.add(new Err());
  10. ts.add(new Err()); //Err没有实现compareTo方法,引发ClassCastException
  11. }
  12. }

大部分类在实现CompareTo方法时,需要将被比较对象强制转换成相同类型。因此向TreeSet中添加的应该是同一个类的对象,否则引发ClassCastException。

  1. import java.util.*;
  2. public class TreeSetErrorTest2
  3. {
  4. public static void main(String[] args)
  5. {
  6. TreeSet ts = new TreeSet();
  7. // 向TreeSet集合中添加两个对象
  8. ts.add(new String("疯狂Java讲义"));
  9. ts.add(new Date()); // 会引发异常
  10. }
  11. }

如果希望TreeSet能正常运作,TreeSet只能添加同一种类型的对象。
TreeSet判断对象相等:通过compareTo方法比较是否返回0

  1. import java.util.*;
  2. class Z implements Comparable
  3. {
  4. int age;
  5. public Z(int age)
  6. {
  7. this.age = age;
  8. }
  9. // 重写equals()方法,总是返回true
  10. public boolean equals(Object obj)
  11. {
  12. return true;
  13. }
  14. // 重写了compareTo(Object obj)方法,总是返回1,总是认为不相等
  15. public int compareTo(Object obj)
  16. {
  17. return 1;
  18. }
  19. }
  20. public class TreeSetTest2
  21. {
  22. public static void main(String[] args)
  23. {
  24. TreeSet set = new TreeSet();
  25. Z z1 = new Z(6);
  26. set.add(z1);
  27. // 第二次添加同一个对象,输出true,表明添加成功
  28. System.out.println(set.add(z1)); //true
  29. // 下面输出set集合,将看到有两个元素
  30. System.out.println(set);
  31. // 修改set集合的第一个元素的age变量
  32. ((Z)(set.first())).age = 9;
  33. // 输出set集合的最后一个元素的age变量,将看到也变成了9
  34. System.out.println(((Z)(set.last())).age);
  35. }
  36. }

注意equals方法应该和compareTo方法有一致的结果。

一旦改变了TreeSet集合里可变元素的实例变量,导致它的与其他对象的大小顺序发生改变,但TreeSet不会调整他们的 顺序,可能导致compareTo返回0。当试图删除该对象时,删除会失败,可以删除没有被修改的实例变量、切不与被修改的实例变量重复的变量。执行一次成功删除之后TreeSet会对集合中的元素重新索引,接下来就可以删除TreeSet中的所有元素了。

  1. import java.util.*;
  2. class R implements Comparable
  3. {
  4. int count;
  5. public R(int count)
  6. {
  7. this.count = count;
  8. }
  9. public String toString()
  10. {
  11. return "R[count:" + count + "]";
  12. }
  13. // 重写equals方法,根据count来判断是否相等
  14. public boolean equals(Object obj)
  15. {
  16. if (this == obj)
  17. {
  18. return true;
  19. }
  20. if(obj != null && obj.getClass() == R.class)
  21. {
  22. R r = (R)obj;
  23. return r.count == this.count;
  24. }
  25. return false;
  26. }
  27. // 重写compareTo方法,根据count来比较大小
  28. public int compareTo(Object obj)
  29. {
  30. R r = (R)obj;
  31. return count > r.count ? 1 :
  32. count < r.count ? -1 : 0;
  33. }
  34. }
  35. public class TreeSetTest3
  36. {
  37. public static void main(String[] args)
  38. {
  39. TreeSet ts = new TreeSet();
  40. ts.add(new R(5));
  41. ts.add(new R(-3));
  42. ts.add(new R(9));
  43. ts.add(new R(-2));
  44. // 打印TreeSet集合,集合元素是有序排列的
  45. System.out.println(ts); // -3 -2 5 9
  46. // 取出第一个元素
  47. R first = (R)ts.first();
  48. // 对第一个元素的count赋值
  49. first.count = 20;
  50. // 取出最后一个元素
  51. R last = (R)ts.last();
  52. // 对最后一个元素的count赋值,与第二个元素的count相同
  53. last.count = -2;
  54. // 再次输出将看到TreeSet里的元素处于无序状态,且有重复元素
  55. System.out.println(ts); // 20 -2 5 -2
  56. // 删除实例变量被改变的元素,删除失败
  57. System.out.println(ts.remove(new R(-2))); // false
  58. System.out.println(ts);//20 -2 5 -2
  59. // 删除实例变量没有被改变的元素,删除成功
  60. System.out.println(ts.remove(new R(5))); // true,之后会重新索引,可以删除所有元素了
  61. System.out.println(ts);// 20 -2 -2
  62. }
  63. }

2. 定制排序
如果需要实现定制排序,例如降序,需要在创建TreeSet集合对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。可以用Lambda表达式代替Comparator对象,这时候无需实现Comparable接口。

  1. import java.util.*;
  2. class M
  3. {
  4. int age;
  5. public M(int age)
  6. {
  7. this.age = age;
  8. }
  9. public String toString()
  10. {
  11. return "M[age:" + age + "]";
  12. }
  13. }
  14. public class TreeSetTest4
  15. {
  16. public static void main(String[] args)
  17. {
  18. // 此处Lambda表达式的目标类型是Comparator
  19. TreeSet ts = new TreeSet((o1 , o2) ->
  20. {
  21. M m1 = (M)o1;
  22. M m2 = (M)o2;
  23. // 根据M对象的age属性来决定大小,age越大,M对象反而越小
  24. return m1.age > m2.age ? -1
  25. : m1.age < m2.age ? 1 : 0;
  26. });
  27. ts.add(new M(5));
  28. ts.add(new M(-3));
  29. ts.add(new M(9));
  30. System.out.println(ts);
  31. }
  32. }

List集合

List集合代表一个元素有序可重复的集合,集合中每个元素都有其对应的顺序索引。List作为Collection接口的子接口,与Set集合相比多了根据索引插入、替换、删除集合元素的方法。

  1. import java.util.*;
  2. public class ListTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. List books = new ArrayList();
  7. // 向books集合中添加三个元素
  8. books.add(new String("轻量级Java EE企业应用实战"));
  9. books.add(new String("疯狂Java讲义"));
  10. books.add(new String("疯狂Android讲义"));
  11. System.out.println(books);
  12. // 将新字符串对象插入在第二个位置
  13. books.add(1 , new String("疯狂Ajax讲义"));
  14. for (int i = 0 ; i < books.size() ; i++ )
  15. {
  16. System.out.println(books.get(i));
  17. }
  18. // 删除第三个元素
  19. books.remove(2);
  20. System.out.println(books);
  21. // 判断指定元素在List集合中位置:输出1,表明位于第二位
  22. System.out.println(books.indexOf(new String("疯狂Ajax讲义"))); //①
  23. //将第二个元素替换成新的字符串对象
  24. books.set(1, new String("疯狂Java讲义"));
  25. System.out.println(books);
  26. //将books集合的第二个元素(包括)
  27. //到第三个元素(不包括)截取成子集合
  28. System.out.println(books.subList(1 , 2));
  29. }
  30. }

List判断对象相等:equals方法返回true即可。

  1. import java.util.*;
  2. class A
  3. {
  4. public boolean equals(Object obj)
  5. {
  6. return true;
  7. }
  8. }
  9. public class ListTest2
  10. {
  11. public static void main(String[] args)
  12. {
  13. List books = new ArrayList();
  14. books.add(new String("轻量级Java EE企业应用实战"));
  15. books.add(new String("疯狂Java讲义"));
  16. books.add(new String("疯狂Android讲义"));
  17. System.out.println(books);
  18. // 删除集合中A对象,将导致第一个元素被删除
  19. books.remove(new A()); // ①
  20. System.out.println(books);
  21. // 删除集合中A对象,再次删除集合中第一个元素
  22. books.remove(new A()); // ②
  23. System.out.println(books);
  24. }
  25. }

List常用的默认方法sort()需要一个Comparator对象控制秩序,可使用Lambda表达式作为参数;默认方法replaceAll()需要一个函数式接口的UnaryOperator来替换所有集合元素,也可用Lambda表达式作为参数。

  1. import java.util.*;
  2. public class ListTest3
  3. {
  4. public static void main(String[] args)
  5. {
  6. List books = new ArrayList();
  7. // 向books集合中添加4个元素
  8. books.add(new String("轻量级Java EE企业应用实战"));
  9. books.add(new String("疯狂Java讲义"));
  10. books.add(new String("疯狂Android讲义"));
  11. books.add(new String("疯狂iOS讲义"));
  12. // 使用目标类型为Comparator的Lambda表达式对List集合排序
  13. books.sort((o1, o2)->((String)o1).length() - ((String)o2).length());
  14. System.out.println(books);
  15. // 使用目标类型为UnaryOperator的Lambda表达式来替换集合中所有元素
  16. // 该Lambda表达式控制使用每个字符串的长度作为新的集合元素
  17. books.replaceAll(ele->((String)ele).length());
  18. System.out.println(books); // 输出[7, 8, 11, 16]
  19. }
  20. }

Set只提供一个iterator()方法,List提供了listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口。

  1. import java.util.*;
  2. public class ListIteratorTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. String[] books = {
  7. "疯狂Java讲义", "疯狂iOS讲义",
  8. "轻量级Java EE企业应用实战"
  9. };
  10. List bookList = new ArrayList();
  11. for (int i = 0; i < books.length ; i++ )
  12. {
  13. bookList.add(books[i]);
  14. }
  15. ListIterator lit = bookList.listIterator();
  16. while (lit.hasNext())
  17. {
  18. System.out.println(lit.next());
  19. lit.add("-------分隔符-------");
  20. }
  21. System.out.println("=======下面开始反向迭代=======");
  22. while(lit.hasPrevious())
  23. {
  24. System.out.println(lit.previous());
  25. }
  26. }
  27. }

ArrayList和Vector
两个类都是基于数组实现的List类,所以都封装了动态的可以再分配的Object[]数组。ArrayList对象和Vector对象使用initialCapacity参数来设置数组的长度。添加元素超出数组长度时,initialCapacity会自动增加。
Vector提供了一个子类Stack,这也是一个古老的集合,线程安全,性能较差,尽量少用。
区别在于:ArrayList时线程不安全的,Vector是线程安全的。所以Vector性能比ArrayList性能要低。
Vector提供了Stack子类,用于模拟后进先出(LIFO)的“栈”这种数据结构。同样线程安全、性能较差。如果需要实现“栈”,可以使用ArrayDeque。

固定长度的List
介绍数组时的Arrays类提供了asList(Object a)方法,该方法可以把一个数组或者指定个数对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例。Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加、删除该集合里的元素。

  1. import java.util.*;
  2. public class FixedSizeList
  3. {
  4. public static void main(String[] args)
  5. {
  6. List fixedList = Arrays.asList("疯狂Java讲义"
  7. , "轻量级Java EE企业应用实战");
  8. // 获取fixedList的实现类,将输出Arrays$ArrayList
  9. System.out.println(fixedList.getClass());
  10. // 使用方法引用遍历集合元素
  11. fixedList.forEach(System.out::println);
  12. // 试图增加、删除元素都会引发UnsupportedOperationException异常
  13. fixedList.add("疯狂Android讲义");
  14. fixedList.remove("疯狂Java讲义");
  15. }
  16. }

Queue集合

Queue用于模拟“先进先出”(FIFO)队列这种数据结构,通常队列不允许随机访问队列中的元素。Queue接口有一个PriorityQueue实现类和一个Deque接口,后者既可以当成队列使用,也可以当栈使用。

PriorityQueue实现类
比较标准的队列实现类--保存队列元素的顺序按照队列元素的大小重新排列。这是违反队列的先进先出的原则的

  1. import java.util.*;
  2. public class PriorityQueueTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. PriorityQueue pq = new PriorityQueue();
  7. // 下面代码依次向pq中加入四个元素
  8. pq.offer(6);
  9. pq.offer(-3);
  10. pq.offer(20);
  11. pq.offer(18);
  12. // 输出pq队列,并不是按元素的加入顺序排列
  13. System.out.println(pq); // 输出[-3, 6, 20, 18]
  14. // 访问队列第一个元素,其实就是队列中最小的元素:-3
  15. System.out.println(pq.poll());
  16. }
  17. }

没有从小到大排序是因为受了toString方法返回值的影响。如果是使用poll()方法,则严格从小到大。
PriorityQueue两种排序方式:自然排序、定制排序,详细参考TreeSet。

Deque接口和ArrayDeque实现类
Deque接口代表一个双端队列,还可以当成栈使用。Deque接口提供了典型的实现类ArrayDeque,这是基于数组实现的双端队列。
ArrayDeque可作为“栈”,也可作为“队列”
注意:ArrayList和ArrayDeque两个集合类的实现机制基本相似,底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,当集合元素超出了数组的容量时,系统会在底层重新分配一个Object[]数组来存储元素。
ArrayDeque作为栈:

  1. import java.util.*;
  2. public class ArrayDequeStack
  3. {
  4. public static void main(String[] args)
  5. {
  6. ArrayDeque stack = new ArrayDeque();
  7. // 依次将三个元素push入"栈"
  8. stack.push("疯狂Java讲义");
  9. stack.push("轻量级Java EE企业应用实战");
  10. stack.push("疯狂Android讲义");
  11. // 输出:[疯狂Android讲义, 轻量级Java EE企业应用实战, 疯狂Java讲义]
  12. System.out.println(stack);
  13. // 访问第一个元素,但并不将其pop出"栈",输出:疯狂Android讲义
  14. System.out.println(stack.peek());
  15. // 依然输出:[疯狂Android讲义, 疯狂Java讲义, 轻量级Java EE企业应用实战]
  16. System.out.println(stack);
  17. // pop出第一个元素,输出:疯狂Android讲义
  18. System.out.println(stack.pop());
  19. // 输出:[轻量级Java EE企业应用实战, 疯狂Java讲义]
  20. System.out.println(stack);
  21. }
  22. }

ArrayDeque作为队列

  1. import java.util.*;
  2. public class ArrayDequeQueue
  3. {
  4. public static void main(String[] args)
  5. {
  6. ArrayDeque queue = new ArrayDeque();
  7. // 依次将三个元素加入队列
  8. queue.offer("疯狂Java讲义");
  9. queue.offer("轻量级Java EE企业应用实战");
  10. queue.offer("疯狂Android讲义");
  11. // 输出:[疯狂Java讲义, 轻量级Java EE企业应用实战, 疯狂Android讲义]
  12. System.out.println(queue);
  13. // 访问队列头部的元素,但并不将其poll出队列"栈",输出:疯狂Java讲义
  14. System.out.println(queue.peek());
  15. // 依然输出:[疯狂Java讲义, 轻量级Java EE企业应用实战, 疯狂Android讲义]
  16. System.out.println(queue);
  17. // poll出第一个元素,输出:疯狂Java讲义
  18. System.out.println(queue.poll());
  19. // 输出:[轻量级Java EE企业应用实战, 疯狂Android讲义]
  20. System.out.println(queue);
  21. }
  22. }

LinkedList实现类
这是一个List接口的实现类,所以可以根据索引来随机访问集合中的元素。除此之外还实现了Deque接口,可作为双端队列。双端队列栈的头部和尾部分别对应队列的头部和尾部。

  1. import java.util.*;
  2. public class LinkedListTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. LinkedList books = new LinkedList();
  7. // 将字符串元素加入队列的尾部
  8. books.offer("book1");
  9. books.offer("boook2");
  10. // 将一个字符串元素加入栈的顶部
  11. books.push("book3");
  12. books.push("book4");
  13. // 将字符串元素添加到队列的头部(相当于栈的顶部)
  14. books.offerFirst("book5");
  15. books.offerFirst("book6");
  16. // 以List的方式(按索引访问的方式)来遍历集合元素
  17. for (int i = 0; i < books.size() ; i++ )
  18. {
  19. System.out.println("遍历中:" + books.get(i));
  20. }
  21. // 访问、并不删除栈顶的元素 book6
  22. System.out.println(books.peekFirst());
  23. // 访问、并不删除队列的最后一个元素 book2
  24. System.out.println(books.peekLast());
  25. // 将栈顶的元素弹出“栈” book6
  26. System.out.println(books.pop());
  27. // 下面输出将看到队列中第一个元素被删除
  28. System.out.println(books);
  29. // 访问、并删除队列的最后一个元素book2
  30. System.out.println(books.pollLast());
  31. System.out.println(books);
  32. }
  33. }

LinkedList与ArrayList、ArrayDeque的实现机制完全不同。后两者内部以数组形式来保存集合中的元素,因此随机访问集合元素时性能较好;LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合中的元素时性能较差,但在插入、删除元素时性能较好。Vector和后两者都是以数组形式存储元素,,而Vector实现了线程安全,所以性能较差。

各种线性表的性能分析
数组一块连续内存区来保存所有的数组元素,所以在随机访问时性能最好,所有的内部以数组作为底层实现的集合随机访问时性能都比较好。而内部以链表作为底层实现的集合在执行插入、删除操作时性能较好。
List集合使用的几点建议
+ 需要遍历List集合元素时,对于ArrayList、Vector集合,应该使用随机访问方法来遍历集合元素;对于LinkedList应该采用迭代器来遍历。
+ 需要经常执行插入、删除来改变包含大量数据的List集合的大小,可考虑使用LinkedList集合。使用ArrayList需要重新分配内部数组的大小,效果比较差。
+ 如果有多个线程需要同时访问List集合中的元素,开发者可考虑使用Collection将集合包装成线性安全的集合。

Map集合

Map用于保存具有映射关系的数据,包含两组值,一组是key,一组是value。key不允许重复。Java源码中先实现了Map,然后通过包装一个所有value都为null的Map实现了Set集合。Map中包括一个内部类Entry,该类封装了一个key-value对。

  1. import java.util.*;
  2. public class MapTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. Map map = new HashMap();
  7. // 成对放入多个key-value对
  8. map.put("疯狂Java讲义" , 109);
  9. map.put("疯狂iOS讲义" , 10);
  10. map.put("疯狂Ajax讲义" , 79);
  11. // 多次放入的key-value对中value可以重复
  12. map.put("轻量级Java EE企业应用实战" , 99);
  13. // 放入重复的key时,新的value会覆盖原有的value
  14. // 如果新的value覆盖了原有的value,该方法返回被覆盖的value
  15. System.out.println(map.put("疯狂iOS讲义" , 99)); // 输出10
  16. System.out.println(map); // 输出的Map集合包含4个key-value对
  17. // 判断是否包含指定key
  18. System.out.println("是否包含值为 疯狂iOS讲义 key:"
  19. + map.containsKey("疯狂iOS讲义")); // 输出true
  20. // 判断是否包含指定value
  21. System.out.println("是否包含值为 99 value:"
  22. + map.containsValue(99)); // 输出true
  23. // 获取Map集合的所有key组成的集合,通过遍历key来实现遍历所有key-value对
  24. for (Object key : map.keySet() )
  25. {
  26. // map.get(key)方法获取指定key对应的value
  27. System.out.println(key + "-->" + map.get(key));
  28. }
  29. map.remove("疯狂Ajax讲义"); // 根据key来删除key-value对。
  30. System.out.println(map); // 输出结果不再包含 疯狂Ajax讲义=79 的key-value对
  31. }
  32. }
  1. import java.util.*;
  2. public class MapTest2
  3. {
  4. public static void main(String[] args)
  5. {
  6. Map map = new HashMap();
  7. // 成对放入多个key-value对
  8. map.put("疯狂Java讲义" , 109);
  9. map.put("疯狂iOS讲义" , 99);
  10. map.put("疯狂Ajax讲义" , 79);
  11. // 尝试替换key为"疯狂XML讲义"的value,由于原Map中没有对应的key,
  12. // 因此对Map没有改变,不会添加新的key-value对
  13. map.replace("疯狂XML讲义" , 66);
  14. System.out.println(map);
  15. // 使用原value与参数计算出来的结果覆盖原有的value
  16. map.merge("疯狂iOS讲义" , 10 ,
  17. (oldVal , param) -> (Integer)oldVal + (Integer)param);
  18. System.out.println(map); // "疯狂iOS讲义"的value增大了10
  19. // 当key为"Java"对应的value为null(或不存在时),使用计算的结果作为新value
  20. map.computeIfAbsent("Java" , (key)->((String)key).length());
  21. System.out.println(map); // map中添加了 Java=4 这组key-value对
  22. // 当key为"Java"对应的value存在时,使用计算的结果作为新value
  23. map.computeIfPresent("Java",
  24. (key , value) -> (Integer)value * (Integer)value);
  25. System.out.println(map); // map中 Java=4 变成 Java=16
  26. }
  27. }

HashMap和Hashtable实现类
HashMap和Hashtable类似于ArrayList和Vector的关系,Hashtable是一个古老的Map类。类似Vector尽量少用Hashtable,可以通过Collections工具类把HashMap变成线程安全的。
区别:

  1. import java.util.*;
  2. public class NullInHashMap
  3. {
  4. public static void main(String[] args)
  5. {
  6. HashMap hm = new HashMap();
  7. // 试图将两个key为null的key-value对放入HashMap中
  8. hm.put(null , null);
  9. hm.put(null , null); // 无法放入
  10. // 将一个value为null的key-value对放入HashMap中
  11. hm.put("a" , null); // ②
  12. // 输出Map对象
  13. System.out.println(hm);
  14. }
  15. }

HashMap、Hashtable判断key相等:两个key通过equal方法返回true;两个key的hashCode值相等。
HashMap、Hashtable判断value相等:两个对象通过equal方法返回true。

  1. import java.util.*;
  2. class A
  3. {
  4. int count;
  5. public A(int count)
  6. {
  7. this.count = count;
  8. }
  9. // 根据count的值来判断两个对象是否相等。
  10. public boolean equals(Object obj)
  11. {
  12. if (obj == this)
  13. return true;
  14. if (obj != null && obj.getClass() == A.class)
  15. {
  16. A a = (A)obj;
  17. return this.count == a.count;
  18. }
  19. return false;
  20. }
  21. // 根据count来计算hashCode值。
  22. public int hashCode()
  23. {
  24. return this.count;
  25. }
  26. }
  27. class B
  28. {
  29. // 重写equals()方法,B对象与任何对象通过equals()方法比较都返回true
  30. public boolean equals(Object obj)
  31. {
  32. return true;
  33. }
  34. }
  35. public class HashtableTest
  36. {
  37. public static void main(String[] args)
  38. {
  39. Hashtable ht = new Hashtable();
  40. ht.put(new A(60000) , "疯狂Java讲义");
  41. ht.put(new A(87563) , "轻量级Java EE企业应用实战");
  42. ht.put(new A(1232) , new B());
  43. System.out.println(ht);
  44. // 只要两个对象通过equals比较返回true,
  45. // Hashtable就认为它们是相等的value。
  46. // 由于Hashtable中有一个B对象,
  47. // 它与任何对象通过equals比较都相等,所以下面输出true。
  48. System.out.println(ht.containsValue("测试字符串")); // 输出true
  49. // 只要两个A对象的count相等,它们通过equals比较返回true,且hashCode相等
  50. // Hashtable即认为它们是相同的key,所以下面输出true。
  51. System.out.println(ht.containsKey(new A(87563))); // 输出true
  52. // 下面语句可以删除最后一个key-value对
  53. ht.remove(new A(1232));
  54. System.out.println(ht);
  55. }
  56. }

尽量不要使用可变对象作为HashMap、Hashtable的key,如果确实需要,尽量不要在程序中修改作为key的可变对象。

LinkedHashMap实现类
LinkedHashMap也是用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,其余key-value对的插入顺序保持一致。因为需要维护元素的插入顺序,性能不如HashMap,但在迭代访问Map里的全部元素时将有较好的性能。

  1. import java.util.*;
  2. public class HashMapErrorTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. HashMap ht = new HashMap();
  7. // 此处的A类与前一个程序的A类是同一个类
  8. ht.put(new A(60000) , "疯狂Java讲义");
  9. ht.put(new A(87563) , "轻量级Java EE企业应用实战");
  10. // 获得Hashtable的key Set集合对应的Iterator迭代器
  11. Iterator it = ht.keySet().iterator();
  12. // 取出Map中第一个key,并修改它的count值
  13. A first = (A)it.next();
  14. first.count = 87563;
  15. // 输出{A@1560b=疯狂Java讲义, A@1560b=轻量级Java EE企业应用实战}
  16. System.out.println(ht);
  17. // 只能删除没有被修改过的key所对应的key-value对
  18. ht.remove(new A(87563));
  19. System.out.println(ht);
  20. // 无法获取剩下的value,下面两行代码都将输出null。
  21. System.out.println(ht.get(new A(87563))); //输出null
  22. System.out.println(ht.get(new A(60000))); //输出null
  23. }
  24. }

LinkedHashMap实现类
使用双向链表维护key-value对。因为需要维护元素的插入顺序,性能低于HaspMap。

  1. import java.util.*;
  2. public class LinkedHashMapTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. LinkedHashMap scores = new LinkedHashMap();
  7. scores.put("语文" , 80);
  8. scores.put("英文" , 82);
  9. scores.put("数学" , 76);
  10. // 调用forEach方法遍历scores里的所有key-value对
  11. scores.forEach((key, value) -> System.out.println(key + "-->" + value));
  12. }
  13. }

使用Properties读写属性文件
Properties类是Hashtable类的子类,该对象在处理属性文件时特别方便。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件,也可以把属性文件中的属性名=属性值加载到Map对象中。由于属性文件里的属性名、属性值只能是字符串类型,所以Properties里的key、value都是字符串类型

  1. import java.util.*;
  2. import java.io.*;
  3. public class PropertiesTest
  4. {
  5. public static void main(String[] args)
  6. throws Exception
  7. {
  8. Properties props = new Properties();
  9. // 向Properties中增加属性
  10. props.setProperty("username" , "yeeku");
  11. props.setProperty("password" , "123456");
  12. // 将Properties中的key-value对保存到a.ini文件中
  13. props.store(new FileOutputStream("a.ini")
  14. , "comment line"); //写入文件
  15. // 新建一个Properties对象
  16. Properties props2 = new Properties();
  17. // 向Properties中增加属性
  18. props2.setProperty("gender" , "male");
  19. // 将a.ini文件中的key-value对追加到props2中
  20. props2.load(new FileInputStream("a.ini") ); //读取
  21. System.out.println(props2);
  22. }
  23. }

SortedMap和TreeMap实现类
Set接口派生出SortedSet子接口,TreeSet接口也有一个TreeSet实现类。
Map接口派生出SortedMap子接口,SortedMap接口也有一个TreeMap实现类。
两种排序方式:自然排序、定制排序,同TreeSet。
TreeMap判断key相等:两个key通过compareTo方法返回0。但是自定义类时需要equals方法和compareTo方法返回的结果一致。

  1. import java.util.*;
  2. class R implements Comparable
  3. {
  4. int count;
  5. public R(int count)
  6. {
  7. this.count = count;
  8. }
  9. public String toString()
  10. {
  11. return "R[count:" + count + "]";
  12. }
  13. // 根据count来判断两个对象是否相等。
  14. public boolean equals(Object obj)
  15. {
  16. if (this == obj)
  17. return true;
  18. if (obj != null && obj.getClass() == R.class)
  19. {
  20. R r = (R)obj;
  21. return r.count == this.count;
  22. }
  23. return false;
  24. }
  25. // 根据count属性值来判断两个对象的大小。
  26. public int compareTo(Object obj)
  27. {
  28. R r = (R)obj;
  29. return count > r.count ? 1 :
  30. count < r.count ? -1 : 0;
  31. }
  32. }
  33. public class TreeMapTest
  34. {
  35. public static void main(String[] args)
  36. {
  37. TreeMap tm = new TreeMap();
  38. tm.put(new R(3) , "轻量级Java EE企业应用实战");
  39. tm.put(new R(-5) , "疯狂Java讲义");
  40. tm.put(new R(9) , "疯狂Android讲义");
  41. System.out.println(tm);
  42. // 返回该TreeMap的第一个Entry对象
  43. System.out.println(tm.firstEntry());
  44. // 返回该TreeMap的最后一个key值
  45. System.out.println(tm.lastKey());
  46. // 返回该TreeMap的比new R(2)大的最小key值。
  47. System.out.println(tm.higherKey(new R(2)));
  48. // 返回该TreeMap的比new R(2)小的最大的key-value对。
  49. System.out.println(tm.lowerEntry(new R(2)));
  50. // 返回该TreeMap的子TreeMap 介于-1--4之间的key
  51. System.out.println(tm.subMap(new R(-1) , new R(4)));
  52. }
  53. }

Map实现类的性能分析
HashMap和Hashtable实现机制几乎一样,但是后者是线程安全的,HashMap通常比Hashtable快。
TreeMap比HashMap和Hashtable慢,因为其底层采用红黑树管理key-value对。优势在于:key-value对总是处于有序状态,无须专门进行排序操作。
以上应该多考虑使用HashMap。
LinkedHashMap比HashMap慢一点,因为需要维护链表维持添加顺序 。

操作集合的工具类

Collections提供了大量方法对Set、List、Map进行排序、查询、修改操作。

排序

  1. public class SortTest
  2. {
  3. public static void main(String[] args)
  4. {
  5. ArrayList nums = new ArrayList();
  6. nums.add(2);
  7. nums.add(-5);
  8. nums.add(3);
  9. nums.add(0);
  10. System.out.println(nums); // 输出:[2, -5, 3, 0]
  11. Collections.reverse(nums); // 将List集合元素的次序反转
  12. System.out.println(nums); // 输出:[0, 3, -5, 2]
  13. Collections.sort(nums); // 将List集合元素的按自然顺序排序
  14. System.out.println(nums); // 输出:[-5, 0, 2, 3]
  15. Collections.shuffle(nums); // 将List集合元素的按随机顺序排序
  16. System.out.println(nums); // 每次输出的次序不固定
  17. }
  18. }

查找替换

  1. import java.util.*;
  2. public class SearchTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. ArrayList nums = new ArrayList();
  7. nums.add(2);
  8. nums.add(-5);
  9. nums.add(3);
  10. nums.add(0);
  11. System.out.println(nums); // 输出:[2, -5, 3, 0]
  12. System.out.println(Collections.max(nums)); // 输出最大元素,将输出3
  13. System.out.println(Collections.min(nums)); // 输出最小元素,将输出-5
  14. Collections.replaceAll(nums , 0 , 1); // 将nums中的0使用1来代替
  15. System.out.println(nums); // 输出:[2, -5, 3, 1]
  16. // 判断-5在List集合中出现的次数,返回1
  17. System.out.println(Collections.frequency(nums , -5));
  18. Collections.sort(nums); // 对nums集合排序
  19. System.out.println(nums); // 输出:[-5, 1, 2, 3]
  20. //只有排序后的List集合才可用二分法查询,输出3
  21. System.out.println(Collections.binarySearch(nums , 3));
  22. }
  23. }

同步控制
HashSet、TreeSet、ArrayList、ArrayDeque、linkedlist、HashMap、TreeMap都是线程不安全的。Collections提供了许多类方法把他们包装成线程同步集合。

  1. import java.util.*;
  2. public class SynchronizedTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 下面程序创建了四个线程安全的集合对象
  7. Collection c = Collections
  8. .synchronizedCollection(new ArrayList());
  9. List list = Collections.synchronizedList(new ArrayList());
  10. Set s = Collections.synchronizedSet(new HashSet());
  11. Map m = Collections.synchronizedMap(new HashMap());
  12. }
  13. }

设置不可变集合

  1. import java.util.*;
  2. public class UnmodifiableTest
  3. {
  4. public static void main(String[] args)
  5. {
  6. // 创建一个空的、不可改变的List对象
  7. List unmodifiableList = Collections.emptyList();
  8. // 创建一个只有一个元素,且不可改变的Set对象
  9. Set unmodifiableSet = Collections.singleton("疯狂Java讲义");
  10. // 创建一个普通Map对象
  11. Map scores = new HashMap();
  12. scores.put("语文" , 80);
  13. scores.put("Java" , 82);
  14. // 返回普通Map对象对应的不可变版本
  15. Map unmodifiableMap = Collections.unmodifiableMap(scores);
  16. // 下面任意一行代码都将引发UnsupportedOperationException异常
  17. unmodifiableList.add("测试元素"); //异常
  18. unmodifiableSet.add("测试元素"); //异常
  19. unmodifiableMap.put("语文" , 90); //异常
  20. }
  21. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注