一、泛型
 
      我们可以在类的声明处增加泛型列表,如:<T,E,V>。
 
      此处,字符可以是任何标识符,一般采用这3个字母。
 
【示例9-1】泛型类的声明
 
|   1  2  3  4  5  6  7  8  9  10   |   class MyCollection<E> {// E:表示泛型;      Object[] objs = new Object[5];       public E get(int index) {// E:表示泛型;          return (E) objs[index];      }      public void set(E e, int index) {// E:表示泛型;          objs[index] = e;      }  }   | 
 
      泛型E像一个占位符一样表示“未知的某个数据类型”,我们在真正调用的时候传入这个“数据类型”。
 
【示例9-2】泛型类的应用
 
|   1  2  3  4  5  6  7  8  9  10   |   public class TestGenerics {      public static void main(String[] args) {          // 这里的”String”就是实际传入的数据类型;          MyCollection<String> mc = new MyCollection<String>();          mc.set("aaa", 0);          mc.set("bbb", 1);          String str = mc.get(1); //加了泛型,直接返回String类型,不用强制转换;          System.out.println(str);      }  }   | 
 
 
二、Collection接口
 
  Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。
 
表9-1 Collection接口中定义的方法
 

 
      由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。我们下一节中,通过ArrayList实现类来测试上面的方法。
 
 
三、List特点和常用方法
 
List是有序、可重复的容器。
 
      有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。
 
      可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。
 
      除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法,参见下表:
 
表9-2List接口中定义的方法
 

 
      List接口常用的实现类有3个:ArrayList、LinkedList和Vector。
 
【示例9-4】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   |   public class TestList {      /**       * 测试add/remove/size/isEmpty/contains/clear/toArrays等方法       */      public static void test01() {          List<String> list = new ArrayList<String>();          System.out.println(list.isEmpty()); // true,容器里面没有元素          list.add("高淇");          System.out.println(list.isEmpty()); // false,容器里面有元素          list.add("高小七");          list.add("高小八");          System.out.println(list);          System.out.println("list的大小:" + list.size());          System.out.println("是否包含指定元素:" + list.contains("高小七"));          list.remove("高淇");          System.out.println(list);          Object[] objs = list.toArray();          System.out.println("转化成Object数组:" + Arrays.toString(objs));          list.clear();          System.out.println("清空所有元素:" + list);      }      public static void main(String[] args) {          test01();      }  }   | 
 
      执行结果如图9-3所示:
 

 
【示例9-5】两个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 class TestList {      public static void main(String[] args) {          test02();      }      /**       * 测试两个容器之间元素处理       */      public static void test02() {          List<String> list = new ArrayList<String>();          list.add("高淇");          list.add("高小七");          list.add("高小八");           List<String> list2 = new ArrayList<String>();          list2.add("高淇");          list2.add("张三");          list2.add("李四");          System.out.println(list.containsAll(list2)); //false list是否包含list2中所有元素          System.out.println(list);          list.addAll(list2); //将list2中所有元素都添加到list中          System.out.println(list);          list.removeAll(list2); //从list中删除同时在list和list2中存在的元素          System.out.println(list);          list.retainAll(list2); //取list和list2的交集          System.out.println(list);      }  }   | 
 
      执行结果如图9-4所示:
 

 
图9-4 示例9-5运行效果图
 
【示例9-6】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 class TestList {      public static void main(String[] args) {          test03();      }      /**       * 测试List中关于索引操作的方法       */      public static void test03() {          List<String> list = new ArrayList<String>();          list.add("A");          list.add("B");          list.add("C");          list.add("D");          System.out.println(list); // [A, B, C, D]          list.add(2, "高");          System.out.println(list); // [A, B, 高, C, D]          list.remove(2);          System.out.println(list); // [A, B, C, D]          list.set(2, "c");          System.out.println(list); // [A, B, c, D]          System.out.println(list.get(1)); // 返回:B          list.add("B");          System.out.println(list); // [A, B, c, D, B]          System.out.println(list.indexOf("B")); // 1 从头到尾找到第一个"B"          System.out.println(list.lastIndexOf("B")); // 4 从尾到头找到第一个"B"      }  }   | 
 
      执行结果如图9-5所示:
 

 
 
三、Map接口-HashMap和HashTable
 
Map就是用来存储“键(key)-值(value) 对”的
 
Map类中存储的“键值对”通过键来标识,所以“键对象”不能重复(根据equals方法),否则新的会覆盖旧的
 
Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等
 
HashMap底层实现采用了哈希表,这是一种非常重要的数据结构。希表的本质就是“数组+链表”
 
Entry[] table 就是HashMap的核心数组结构,我们也称之为“位桶数组”。一个Entry对象存储了:
 
- key:键对象,value:值对象
 - next:下一个节点
 - hash: 键对象的hash值
 
 
Entry[]数组的结构:
 

 
存储数据过程put(key,value)
 

 
  当添加一个元素(key-value)时,首先计算key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,就形成了链表,同一个链表上的Hash值是相同的,所以说数组存放的是链表。
 
HashMap
 
 HashMap采用哈希算法实现,是Map接口最常用的实现类。 由于底层采用了哈希表存储数据,我们要求键不能重复,如果发生重复,新的键值对会替换旧的键值对。 HashMap在查找、删除、修改方面都有非常高的效率。
 
【示例9-7】Map接口中的常用方法
 
|   1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21   |   public class TestMap {      public static void main(String[] args) {          Map<Integer, String> m1 = new HashMap<Integer, String>();          Map<Integer, String> m2 = new HashMap<Integer, String>();          m1.put(1, "one");          m1.put(2, "two");          m1.put(3, "three");          m2.put(1, "一");          m2.put(2, "二");          System.out.println(m1.size());          System.out.println(m1.containsKey(1));          System.out.println(m2.containsValue("two"));          m1.put(3, "third"); //键重复了,则会替换旧的键值对          Map<Integer, String> m3 = new HashMap<Integer, String>();          m3.putAll(m1);          m3.putAll(m2);          System.out.println("m1:" + m1);          System.out.println("m2:" + m2);          System.out.println("m3:" + m3);      }  }   | 
 
      执行结果如图9-11所示:
 

 
HashTable
 
      HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。
 
HashMap与HashTable的区别
 
      1. HashMap: 线程不安全,效率高。允许key或value为null。
 
      2. HashTable: 线程安全,效率低。不允许key或value为null。
 
 
四、Set接口-HashSet基本使用
 
大家在做下面练习时,重点体会“Set是无序、不可重复”的核心要点。
 
【示例9-9】HashSet的使用
 
|   1  2  3  4  5  6  7  8  9  10  11  12  13  14   |   public class Test {      public static void main(String[] args) {          Set<String> s = new HashSet<String>();          s.add("hello");          s.add("world");          System.out.println(s);          s.add("hello"); //相同的元素不会被加入          System.out.println(s);          s.add(null);          System.out.println(s);          s.add(null);          System.out.println(s);      }  }   | 
 
      执行结果如图9-24所示:
 

 
 
五、Collections工具类
 
类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。
 
      1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
 
      2. void shuffle(List) //对List容器内的元素进行随机排列。
 
      3. void reverse(List) //对List容器内的元素进行逆续排列 。
 
      4. void fill(List, Object) //用一个特定的对象重写整个List容器。
 
      5. int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象。
 
【示例9-23】Collections工具类的常用方法
 
|   1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18   |   public class Test {      public static void main(String[] args) {          List<String> aList = new ArrayList<String>();          for (int i = 0; i < 5; i++){              aList.add("a" + i);          }          System.out.println(aList);          Collections.shuffle(aList); // 随机排列          System.out.println(aList);          Collections.reverse(aList); // 逆续          System.out.println(aList);          Collections.sort(aList); // 排序          System.out.println(aList);          System.out.println(Collections.binarySearch(aList, "a2"));           Collections.fill(aList, "hello");          System.out.println(aList);      }  }   | 
 
      执行结果如图9-31所示:
 
