JavaSE 13 集合与泛型

来源:互联网 发布:linux mint哪个版本 编辑:程序博客网 时间:2024/06/05 14:06

1、概念

       用于保存一组数的容器。

       数组也是保存一组数的容器。但其不足:⑴ 长度必须提起指定,而且一旦固定将不能更改。⑵ 管理对象类型时,实现增删改查的逻辑,相对比较麻烦。
       集合则弥补了数组的不足:⑴ 不用指定长度,可以自动扩容。⑵ 实现增删改查的逻辑,相对比较容易。所以比较适合存储对象类型。

2、体系图

集合的体系图

3、Collection接口

单列集合,有两个子接口:List和Set

常用方法


add


boolean add(E e);
添加单个元素到集合中

addAll


boolean addAll(Collection<? extends E> c);
将一个集合中的元素追加到集合中

remove


boolean remove(Object o);
从集合中删除单个元素。注意:如果集合中存在多个相同的元素,则删除离集合首最近的元素

removeAll


boolean removeAll(Collection<?> c);
从集合中删除一个给定的集合中的元素。注意:只会删除两个集合中相同的元素

contains


boolean contains(Object o);
查找集合中是否存在某个元素

size


int size();
返回当前集合中元素的个数

isEmpty


boolean isEmpty();
判断当前集合中是否有元素【元素个数是否为0】

clear


void clear();
移除集合中所有的元素

toArray

Object[] toArray();
将集合转换为数组

iterator


Iterator iterator();
获取集合的迭代器对象。

iterator 的方法


⑴ hasNext

boolean hasNext();
如果仍有元素可以迭代,则返回 true,否则返回false

⑵ next

E next();
返回当前迭代的元素

⑶ remove

void remove();
移除当前迭代的元素
注意:调用此方法时,必须先调用迭代器的next方法,否则报错:java.lang.IllegalStateException

ArrayList list = new ArrayList();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);Iterator it = list.iterator();while (it.hasNext()) {  Integer i = (Integer) it.next(); // 向下转型  if (2 == i) { // 移除值为2的元素    it.remove();  }}System.out.println(list); // [1, 3, 4, 5]

迭代器的工作原理


⑴ Iterator自带指针,默认是在集合的最上方(第一个元素的上方)。
⑵ 通过hasNext方法判断下一个集合位置是否有元素。如果有则返回true,如果没有则返回false。
⑶ 每次调用next方法时,将指针下移一位,同时取出对应的元素。直到没有元素可以取出为止。

  注意:① 每次只能下移,不能上移。        ② 每次只能移动一位。

Collection的遍历方法

Collection c = new 具体的实现类();

使用迭代器

Iterator iterator = c.iterator();while(iterator.hasNext()) { Object obj = iterator.next(); // 操作obj【集合中的每一个元素】}

增强for循环

   for (Object obj : c) {     // 操作obj【集合中的每一个元素】   }

注意:当从集合中读取元素时,如果没有使用泛型,则遍历出的元素类型为Object

4、List接口

特点


⑴ 有序。注意:这里的有序是指元素取出的顺序和添加、插入的顺序是一致的
⑵ 集合内的元素可以重复
⑶ 支持索引

List接口所特有的方法


add


void add(int index, E element);
插入。将某个对象添加到集合的指定索引位置处

remove


E remove(int index);
移除。移除集合的指定索引位置处的元素

set


E set(int index, E element);
更新。修改集合的指定索引位置处的元素

get


E get(int index);
查找。返回集合的指定索引位置处的元素

indexOf


int indexOf(Object o);
查找指定对象在集合中首次出现的索引位置。如果没找到则返回-1

lastIndexOf


int lastIndexOf(Object o);
查找指定对象在集合中最后一次出现的索引位置。如果没找到则返回-1

5、ArrayList类【List接口的实现类】

特点


可以将其理解为是一个Object类型的数组,通过源码可以看出:

private transient Object[] elementData; // 属性public ArrayList() { // 无参构造 this(10); // 调用有参构造}public ArrayList(int initialCapacity) { // 有参构造  // ... 省略  this.elementData = new Object[initialCapacity];}

       如果创建对象时,调用ArrayList的无参构造,其无参构造会调用有参构造,最终开辟长度为10的Object类型的数组。当使用add方法时,如果长度超过了现有长度,则会继续扩充数组长度,新增加的长度为现有长度的一半。
       例如:新创建ArrayList对象的长度为10,添加了11个元素,则这时会扩充为15长度的数组,即新加5个长度(10 / 2)。如果再添加5个元素,则长度扩为22,即新加7个长度(15 / 2)。依此类推。

构造方法


⑴ ArrayList()
构造一个初始容量为 10 的空列表【数组】
⑵ ArrayList(int initialCapacity)
构造一个指定初始容量的列表

方法使用示例 ⑴

ArrayList al = new ArrayList();// 添加一个元素al.add("abc");al.add(null); // 可以放nullArrayList list = new ArrayList();list.add(1);list.add(false);list.add(3.7f);list.add(2);list.add('男');list.add(2);// 添加一个集合中的元素al.add(list);// 插入元素al.add(3, "Java");// 移除元素al.remove("abc"); // 移除某对象al.remove(2); // 移除下标为2的元素ArrayList list2 = new ArrayList();list2.add(false);list2.add('女');list2.add(null);// 移除一个集合中的元素【前提是这两个集合中有相同的元素】al.removeAll(list2);// 修改元素al.set(0, "我是修改后的元素");// 查找元素Object obj = al.get(0); // 没有使用泛型,得到的集合元素的类型是ObjectSystem.out.println("角标为0的元素:" + obj);// 是否包含某元素boolean contain = al.contains(true);System.out.println(contain);// 查看当前集合中元素的个数System.out.println(al.size());// 判断当前集合中元素个数是否为0System.out.println(al.isEmpty());// 查找元素的下标int i1 = al.indexOf('男');System.out.println(i1);int i2 = al.lastIndexOf(2);System.out.println(i2);System.out.println(al); // 因为集合重写了toString 方法

方法使用示例 ⑵

ArrayList list = new ArrayList();list.add(1);list.add(2);list.add(3);list.add(4);Object[] obj = list.toArray(); // 集合转数组int len = obj.length;for (int i = 0; i < len; i++) {  System.out.println(obj[i]);}

遍历方法

ArrayList list0 = new ArrayList();list0.add("abc");list0.add(null);ArrayList list1 = new ArrayList();list1.add(1);list1.add(false);list1.add(3.7f);list1.add(2);list1.add('男');ArrayList list2 = new ArrayList();list2.add(false);list2.add('女');list2.add(null);

使用迭代器

import java.util.Iterator; // 导包Iterator it = list0.iterator();while (it.hasNext()) {  System.out.println(it.next());}

使用增强for循环

for (Object obj : list1) {  System.out.println(obj);}

使用普通for循环

for (int i = 0; i < list2.size(); i++) {  System.out.println(list2.get(i));}

6、LinkedList类【List接口的实现类】

特点


底层是链表结构。其元素不是连续的。

构造方法


public LinkedList() {} // 构造一个空链表

LinkedList的特有方法


addFirst


public void addFirst(E e) {}
添加某元素到集合首位

addLast


public void addLast(E e) {}
添加某元素到集合末位

removeFirst


public E removeFirst() {}
移除集合的首位元素

removeLast


public E removeLast() {}
移除集合的末位元素

getFirst


public E getFirst() {}
获取集合的首位元素

getLast


public E getLast() {}
获取集合的末位元素

LinkedList list = new LinkedList();list.add(1);list.add(2);list.add(3);list.add(4);System.out.println(list.getFirst()); // 1 【获取首位元素】System.out.println(list.getLast()); // 4 【获取末位元素】list.removeFirst(); // 移除首位元素list.removeLast(); // 移除末位元素list.addFirst('a'); // 添加字符a 到首位list.addLast("b"); // 添加字符串b 到末位System.out.println(list); // [a, 2, 3, b]

LinkedList和ArrayList的对比

               LinkedList            ArrayList底层结构          链表结构              数组结构增删效率            较高                 较低改查效率            较低                 较高

7、Vector类【List接口的实现类】

概念


Vector 类可以实现可增长的对象数组。
Java源码中对ArrayList类的描述:此类大致上等同于 Vector 类,除了此类是不同步的。
所以当需要用Vector来解决问题时(一般用于面试),可以先用ArrayList做出,再将ArrayList类名改名为Vector类名。

Vector和ArrayList的对比

                  Vector             ArrayList底层结构           数组结构             数组结构线程是否安全        安全                不安全是否同步            同步                不同步 效率               低                   高出现的版本         JDK1.0               JDK1.2

8、Set接口

特点


⑴ 无序。注意:这里的无序是指元素取出的顺序和添加、插入的顺序是不一致的
⑵ 不允许添加重复的元素
⑶ 不支持索引

9、HashSet类【Set接口的实现类】

特点

底层是哈希表结构【实际上是一个HashMap的实例】。它是无序的,元素不允许重复。允许使用 null 元素。

HashSet是如何过滤重复元素的【面试题】


通过调用hashCode 方法和equals 方法,来过滤重复元素。具体细节:
在添加元素到HashSet中时,首先调用hashCode 方法来判断元素的hash值,是否和集合中已有元素的hash值相同,如果都不同,则直接添加;如果有相同的,则会调用equals 方法来判断这两个元素是否相等,如果相等,则不会添加;如果不等,才会添加。

重写hashCode方法


重写hashCode方法的原因


当使用HashSet存放对象元素时,因为需要调用hashCode方法来判断要添加的对象是否和已存在的对象相同,而直接继承自Object类的hashCode方法,可能达不到我们预期的效果。例如:我们想让两个对象的属性相同,其哈希值也相同。所以这时就需要重写hashCode方法。

重写规则


⑴ 属性值一样的,hash值也一样。
⑵ 属性值不一样的,hash值尽量也不一样。

重写示例

public class Test {  public static void main(String[] args) {    Person p1 = new Person("张三", 17);    Person p2 = new Person("张三", 17);    Person p3 = new Person("李四", 21);    System.out.println(p1.hashCode()); // 775447    System.out.println(p2.hashCode()); // 775447    System.out.println(p3.hashCode()); // 842743  }}class Person { private String name; // 姓名 private int age; // 年龄  @Override  public int hashCode() {    return name.hashCode() + 31 * (1 + age);  }  Person (String name, int age) {    this.name = name;    this.age = age;  }}

10、TreeSet类【Set接口的实现类】

特点


底层是红黑树(二叉树)结构【基于TreeMap的NavigableMap实现】。不允许添加重复元素。其可以对添加的元素进行自然排序,或者根据创建 set 时提供的 Comparator(比较器)进行排序,这具体取决于使用的构造方法。

TreeSet是如何过滤重复元素的


通过比较方法【compareTo或compare方法】的返回值来确定元素是否重复。如果方法的返回值为0,则说明重复了。

自然排序

让自定义类型实现Comparable接口,并实现compareTo方法。

compareTo方法


public int compareTo(T o);

⑴ 实现compareTo方法的步骤

① 让自定义类实现Comparable接口。
② 实现compareTo方法。
③ 首先将传入的参数,向下转型。然后再根据属性做出对应的比较。如果是引用数据类型可以调用属性类型的compareTo方法(如果有);基本数据类型,例如整形,可以用判断运算符进行比较(大于、小于)。

⑵ 调用特点

当添加新元素时,新添加的元素会调用compareTo方法,和集合中已有的元素进行比较。即方法的调用方为新加入的元素,传入的实参为集合中已有元素

class Person implements Comparable {  private String name; // 姓名  private int age; // 年龄  @Override  public int compareTo(Object o) {    Person person = (Person) o; // 强转    if (age < person.age) { // 首先比较年龄      return -1;    } else if (age > person.age) {      return 1;    }    return name.compareTo(person.name); // 如果年龄相同,则比较姓名【调用String类的compareTo方法】  }}

定制排序

在创建TreeSet集合对象时,通过调用有参构造,传入一个Comparator对象,并实现compare方法。

Comparator比较器


    强行对某个集合进行整体排序的比较方法。

Comparator的应用

    创建一个Comparator的内部类【通常是在创建TreeMap的对象时,调用其有参构造,传入一个Comparator对象】,并实现其compare方法。

实现compare方法的步骤

int compare(T o1, T o2);
    首先可以判断o1是否和o2的引用相同,若引用相同则返回0【可以提高效率】。若引用不同则根据具体传入的对象,依次比较两个对象的属性【需要进行向下转型】。如果o1的属性值比o2的属性值小,则返回-1;如果o1的属性比o2的属性值大,则返回1。
    具体示例可以查看下方。

构造方法

TreeSet()

public TreeSet() {}
构造一个新的空TreeSet,该TreeSet根据其元素的自然顺序进行排序。

TreeSet(Comparator comparator)


public TreeSet(Comparator<? super E> comparator) {}
构造一个新的空TreeSet,它根据指定比较器进行排序。在使用时可以直接创建一个匿名内部类。

添加对象元素的注意事项

自定义类型

    自定义类型需要实现Comparable接口,并实现compareTo方法。否则会报错:Exception in thread “main” java.lang.ClassCastException: 自定义的全类名 cannot be cast to java.lang.Comparable

String等系统定义类型

    需要注意的是,当添加新元素到集合中时,会调用新加入的元素的compareTo 方法。如果传入的是系统定义的类型,这时和自定义类型比较就会出错。这是因为系统实现的compareTo方法使用了泛型,强制比较就会报错:java.lang.ClassCastException

import java.util.TreeSet;public class Test {  public static void main(String[] args) {    TreeSet set = new TreeSet();    set.add(new Person("zhang"));    set.add("wang"); // java.lang.ClassCastException: Person cannot be cast to java.lang.String  /*    这是因为将字符串添加到TreeSet时,会调用String的compareTo(String anotherString) 方法,很明显Person和String不是同一个类型  */  }}class Person implements Comparable {  private String name; // 姓名  Person (String name) {    this.name = name;  }  @Override  public int compareTo(Object o) {    Person person = (Person) o;    return name.compareTo(person.name);  }}

TreeSet的使用示例

使用无参构造示例


【此例是先比较年龄,再比较姓名】

import java.util.TreeSet;public class Test {  public static void main(String[] args) {    TreeSet set = new TreeSet();    set.add(new Person("zhang", 17));    set.add(new Person("li", 17));    set.add(new Person("wang", 19));    set.add(new Person("zhang", 21));    System.out.println(set); // [li:17, zhang:17, wang:19, zhang:21]  }}class Person implements Comparable {  private String name; // 姓名  private int age; // 年龄  Person (String name, int age) {    this.name = name;    this.age = age;  }  @Override  public String toString() {    return name + ":" + age;  }  @Override  public int compareTo(Object o) {    if (this == o) { // // 先判断两个对象是否相同【可以提高效率】      return 0;    }    Person person = (Person) o;    if (age < person.age) {      return -1;    } else if (age > person.age) {      return 1;    }    return name.compareTo(person.name);  }}

使用有参构造示例【形参为一个比较器】

import java.util.Comparator;import java.util.TreeSet;public class Test {  public static void main(String[] args) {    TreeSet set = new TreeSet(new Comparator(){ // 匿名内部类      @Override      public int compare(Object o1, Object o2) {        if (o1 == o2) { // 先判断两个对象是否相同【可以提高效率】          return 0;        }        Person p1 = (Person) o1; // 强转        Person p2 = (Person) o2; // 强转        if (p1.getAge() < p2.getAge()) { // 先比较年龄          return -1;        } else if (p1.getAge() > p2.getAge()) {          return 1;        }        return p1.getName().compareTo(p2.getName()); // 当年龄相同时,比较姓名      }    });    set.add(new Person("zhang", 17));    set.add(new Person("li", 17));    set.add(new Person("wang", 19));    set.add(new Person("zhang", 21));    System.out.println(set); // [li:17, zhang:17, wang:19, zhang:21]  }}class Person {  private String name; // 姓名  private int age; // 年龄  Person (String name, int age) {    this.name = name;    this.age = age;  }  public String getName() {    return name;  }  public int getAge() {    return age;  }  @Override  public String toString() {    return name + ":" + age;  }}

TreeSet和HashSet的对比

                      TreeSet                           HashSet 底层结构          红黑树(二叉树)结构                    哈希表结构过滤重复元素   通过compare方法或compareTo方法    通过hashCode方法和equals方法                   是否排序               自然排序                            无序

11、LinkedHashSet【HashSet的子类】

特点


    与HashSet的特点类似,同时其显著的特点就是增加、插入和取出的顺序是一致的【有序的】。其插入元素的效率略低于HashSet,但在迭代访问集合中的全部元素时,有很好的性能。

示例

HashSet set = new HashSet();  set.add(1);  set.add(5);  set.add(11);  set.add(2);  set.add(8);System.out.println(set); // [1, 2, 5, 8, 11] 【已经实现了按照哈希表的排序】LinkedHashSet lhs = new LinkedHashSet();  lhs.add(1);  lhs.add(5);  lhs.add(11);  lhs.add(2);  lhs.add(8);System.out.println(lhs); // [1, 5, 11, 2, 8] 【按照添加的顺序取出】

12、Map接口

特点


⑴ 双列集合。保存键值对的映射。
⑵ 键不能重复,值可以重复

方法列举


put


V put(K key, V value);
添加或更新。当集合中没有指定的键时,该方法的功能是添加;当集合中有指定的键是,该方法的功能是更新。

get


V get(Object key);
根据指定键,返回对应的值。如果找不到返回null。

remove


V remove(Object key);
根据指定键,移除对应的一个映射关系。

containsKey


boolean containsKey(Object key);
判断集合中是否包含指定的键。

containsValue


boolean containsValue(Object value);
判断集合中是否包含指定的值。

size


int size();
返回当前集合中的映射数量。

clear


void clear();
清空集合内的映射。

isEmpty


boolean isEmpty();
判断集合是否为空【集合中的映射数量是否为0】。

keySet


Set keySet();
返回集合中所有的键。返回值类型为Set,其元素为所有的键。

entrySet


Set<Map.Entry<K, V>> entrySet();
返回集合中所有的映射关系。返回值类型为Set,其元素为Entry。

Entry


Entry是Map集合中的一个内部接口【Map.Entry】。它代表一个个的映射关系【映射项(键-值对)】。其声明了操作键和值的方法。

⑴ getKey

K getKey();
获取一个映射关系中对应的键。

⑵ getValue

V getValue();
获取一个映射关系中对应的值。

⑶ setValue

V setValue(V value);
用指定的值替换为当前映射关系中的值。

Map的遍历方法

Map map = new 具体的实现类();

通过keySet方法


Set keys = map.keySet();

⑴ 使用迭代器

Iterator iterator = keys.iterator();while (iterator.hasNext()) {   Object key = iterator.next(); // 获取键   Object value = map.get(key); // 获取值}

⑵ 使用增强for循环

for (Object key : keys) { // 每次遍历得到键   Object value = map.get(key); // 获取值}

通过entrySet方法


Set entrys = map.entrySet();

⑴ 使用迭代器

Iterator iterator = entrys.iterator();while(iterator.hasNext()) {  Map.Entry me = (Map.Entry) iterator.next(); // 得到每一个映射关系  Object key = me.getKey(); // 获取键  Object value = me.getValue(); // 获取值}

⑵ 使用增强for循环

for (Object obj : entrys) {  Map.Entry me = (Map.Entry) obj; // 得到每一个映射关系  Object key = me.getKey(); // 获取键  Object value = me.getValue(); // 获取值}

13、HashMap类【Map类的实现类】

特点

底层是哈希表结构。可以添加null键和null值。当添加自定义类型时,需要重写hashCode和equals方法。

方法使用示例

import java.util.HashMap;public class Test {  public static void main(String[] args) {    HashMap map = new HashMap();    Person p1 = new Person("张三", 14);    Person p2 = new Person("李四", 16);    Person p3 = new Person("王五", 12);    map.put(p1, 60);    map.put(p2, 79);    map.put(p1, 100); // 更新操作 【因为已经存在“张三”这个对象了】    map.put(p3, 89);    map.put(null, null); // 可以放null键 和 null值    System.out.println(map); // {李四:16=79, null=null, 张三:14=100, 王五:12=89}    Person p4 = new Person("赵柳", 10);    // 查找键或值是否存在    System.out.println(map.containsKey(p4)); // false    System.out.println(map.containsValue(100)); // true    // 移除操作    map.remove(p3);    // 查询当前映射数量    System.out.println(map.size()); // 3    // 清空集合    map.clear();    // 集合是否为空    System.out.println(map.isEmpty()); // true  }}/** * 自定义一个Person类 * * 重写了hashCode和equals方法 */class Person {  private String name; // 姓名  private int age; // 年龄  public Person(String name, int age) {    this.name = name;    this.age = age;      }  @Override  public int hashCode() {    return name.hashCode() + 31 * (1 + age);  }  @Override  public boolean equals(Object obj) {    if (this == obj) {      return true;    }    if (!(obj instanceof Person)) {      return false;    }    Person person = (Person) obj;    if (!name.equals(person.name)) {      return false;    } else if (age != person.age) {      return false;    }    return true;  }  @Override  public String toString() {    return name + ":" + age;  }}

遍历方法

HashMap map = new HashMap();    map.put(1, "a");    map.put(5, "e");    map.put(3, "c");    map.put(2, "b");    map.put(4, "d");

通过keySet方法

Set keys = map.keySet();Iterator iterator = keys.iterator(); // 使用迭代器while(iterator.hasNext()) {  Object key = iterator.next();  Object value = map.get(key);  System.out.println(key + " : " + value);}System.out.println();for (Object key : keys) { // 使用增强for循环  Object value = map.get(key);  System.out.println(key + " = " + value);}

通过entrySet方法

Set entrys = map.entrySet();Iterator iterator = entrys.iterator(); // 使用迭代器while (iterator.hasNext()) {  Map.Entry me = (Map.Entry) iterator.next();  Object key = me.getKey();  Object value = me.getValue();  System.out.println(key + " : " + value);}System.out.println();for (Object entry : entrys) { // 使用增强for循环  Map.Entry me = (Map.Entry) entry;  Object key = me.getKey();  Object value = me.getValue();  System.out.println(key + " = " + value);}

Map.Entry接口中的setValue方法

可以实现对当前映射关系中的值的更新操作。

import java.util.HashMap;import java.util.Map;public class Test {  public static void main(String[] args) {    HashMap map = new HashMap();    map.put("A", 1);    map.put("B", 2);    map.put("C", 3);    map.put("D", 4);    map.put("E", 5);    System.out.println(map); // {A=1, B=2, C=3, D=4, E=5}    for (Object entry : map.entrySet()) {      Map.Entry me = (Map.Entry) entry;      Integer value = (Integer) me.getValue();      me.setValue(value + 10); // 替换    }    System.out.println(map); // {A=11, B=12, C=13, D=14, E=15}  }}

14、TreeMap类【Map类的实现类】

特点

基于红黑树(Red-Black tree)的NavigableMap实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。
具体的操作可以参考TreeSet。

TreeMap和HashMap的对比

                                                            TreeMap                                               HashMap         底层结构                                     红黑树(二叉树)结构                                      哈希表结构       键能否自然排序                                        可以                                                  不能添加自定义对象元素的注意事项        需要让自定义类型实现Comparable接口,并实现compareTo方法;           需要重写hashCode和equals方法                                            或创建TreeSet集合对象时,调用有参构造,                                            传入一个Comparator,并实现compare方法。

15、Hashtable类【Map类的实现类】

特点

底层是哈希表结构。不可以添加null键和null值。和HashMap类似,当添加自定义类型时,需要重写hashCode和equals方法。
所以当需要用Hashtable来解决问题时(一般用于面试),可以先用HashMap做出,再将HashMap类名改名为Hashtable类名。注意不要添加null键或null值。

Hashtable和HashMap的对比

                             Hashtable         HashMap     底层结构                 哈希表结构        哈希表结构   线程是否安全                  安全            不安全     是否同步                    同步             同步       效率                      低               高是否允许null键和null值           不允许            允许    出现的版本                  JDK1.0          JDK1.2

16、LinkedHashMap【HashMap的子类】

特点


    与HashMap的特点类似,同时其显著的特点就是增加、插入和取出的顺序是一致的【有序的】。其插入元素的效率略低于HashMap,但在迭代访问集合中的全部元素时,有很好的性能。

示例

HashMap map = new HashMap();  map.put("a", 1);  map.put("e", 10);  map.put("b", 3);  map.put("d", 7);  map.put("c", 6);  System.out.println(map); // {a=1, b=3, c=6, d=7, e=10} 【已经实现了按照哈希表的排序】LinkedHashMap lhm = new LinkedHashMap();  lhm.put("a", 1);  lhm.put("e", 10);  lhm.put("b", 3);  lhm.put("d", 7);  lhm.put("c", 6);System.out.println(lhm); // {a=1, e=10, b=3, d=7, c=6} 【按照添加的顺序取出】

17、判断该用哪种集合类型

该用哪种集合类型

18、Collections类的方法列举

reverse

public static void reverse(List<?> list) {}
反转传入的集合内的元素。只能传入List的实现类。

shuffle

public static void shuffle(List<?> list) {}
随机排列传入的集合内的元素。只能传入List的实现类。

sort

⑴ sort(List<T> list) {}
根据元素的自然顺序对指定列表按升序进行排序。只能传入List的实现类。

⑵ sort(List<T> list, Comparator<? super T> c) {}
根据指定比较器产生的顺序对指定列表进行排序。只能传入List的实现类。

swap

public static void swap(List<?> list, int i, int j) {}
交换传入的集合中的i位置和j位置的元素。位置从0开始。只能传入List的实现类。

max

⑴ max(Collection<? extends T> coll) {}
根据元素的自然顺序,返回传入的集合的最大元素。不能放入Map的实现类。

⑵ max(Collection<? extends T> coll, Comparator<? super T> comp) {}
根据指定比较器产生的顺序,返回传入的集合的最大元素。不能放入Map的实现类。

min

⑴ min(Collection<? extends T> coll) {}
根据元素的自然顺序,返回传入的集合的最小元素。不能放入Map的实现类。

⑵ min(Collection<? extends T> coll, Comparator<? super T> comp) {}
根据指定比较器产生的顺序,返回传入的集合的最小元素。不能放入Map的实现类。

frequency

public static int frequency(Collection<?> c, Object o) {}
返回指定元素在集合中出现的次数。不能放入Map的实现类。

replaceAll

public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) {}
用新的元素替换集合中的旧元素。只能传入List的实现类。

copy

public static <T> void copy(List<? super T> dest, List<? extends T> src) {}
将一个集合【src】中的元素,复制到另一个集合【dest】中。只能传入List的实现类。
注意:dest中的元素个数至少等于src中的元素个数,否则报错。看源码:

  int srcSize = src.size();  if (srcSize > dest.size())    throw new IndexOutOfBoundsException("Source does not fit in dest");

示例:

import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;public class Test {  public static void main(String[] args) {    List list = new ArrayList();    list.add("a");    list.add("e");    list.add("d");    list.add("e");    list.add("b");    list.add("d");    System.out.println(list); // [a, e, d, e, b, d]    Collections.reverse(list); // 反转    System.out.println(list); // [d, b, e, d, e, a]    Collections.shuffle(list); // 随机排序元素位置    System.out.println(list);    // 排序    Collections.sort(list);    System.out.println(list); // [a, b, d, d, e, e]    List list2 = new ArrayList();    list2.add(new Person("wang"));    list2.add(new Person("zhao"));    list2.add(new Person("li"));    list2.add(new Person("qian"));    list2.add(new Person("zhang"));    Comparator comparator = new Comparator(){      @Override      public int compare(Object o1, Object o2) {        Person p1 = (Person) o1;        Person p2 = (Person) o2;        return p1.getName().compareTo(p2.getName());      }    };    Collections.sort(list2, comparator);    System.out.println(list2); // [li, qian, wang, zhang, zhao]    Collections.swap(list2, 1, 4); // 交换1号角标和4号角标的元素位置    System.out.println(list2); // [li, zhao, wang, zhang, qian]    // 获取最值    System.out.println(Collections.max(list)); // g    System.out.println(Collections.min(list)); // a    System.out.println(Collections.max(list2, comparator)); // zhao        System.out.println(Collections.min(list2, comparator)); // li    // 查询相同元素在集合中的个数    System.out.println(Collections.frequency(list, "e")); // 2    Collections.replaceAll(list, "d", "D"); // 替换元素    System.out.println(list); // [a, b, D, D, e, e]    // 复制集合    // List dest = new ArrayList();    // Collections.copy(dest, list); // java.lang.IndexOutOfBoundsException: Source does not fit in dest    List names = new ArrayList();    for (int i = 0; i < list2.size(); i++) {      names.add(null);    }    Collections.copy(names, list2);    System.out.println(names); // [li, zhao, wang, zhang, qian]  }}class Person {  private String name;  public Person(String name) {    this.name = name;  }  public String getName() {    return name;  }  @Override  public String toString() {    return name;  }}

19、泛型

概念

JDK5.0之后出现的新特性,又称为参数化类型。

使用泛型的好处

⑴ 存储数据时,检查添加元素的编译类型,提高了类型的安全性。
⑵ 读取元素时,减少了类型的转换次数,提高了效率。

泛型在集合中的应用

语法规则:
集合类型<参数类型> 集合名 = new 集合类型<参数类型>();
或:
集合类型<参数类型> 集合名 = new 集合类型<>();
可以省略后面尖括号中的参数类型,但不能省略尖括号,这是JDK7.0后出现的。

其他情况:
集合类型 集合名 = new 集合类型<参数类型>();
集合类型<参数类型> 集合名 = new 集合类型();
集合类型 集合名 = new 集合类型();
这些都是为了和旧版本兼容,其默认的类型为Object类型。

注意:泛型指定的类型只能是引用数据类型,不能是基本数据类型。例如可以是Integer,但不能是int。

示例:

import java.util.Map;import java.util.TreeMap;public class Test {  public static void main(String[] args) {    // 泛型应用于集合上    Map<Person, Double> map = new TreeMap<Person, Double>();    map.put(new Person("zhangsan"), 59.6);    map.put(new Person("wangwu"), 78.9);    map.put(new Person("tianqi"), 45.7);    map.put(new Person("zhaoliu"), 89.6);    map.put(new Person("lisi"), 98.2);    for (Map.Entry<Person, Double> me : map.entrySet()) {      Person person = me.getKey();      Double score = me.getValue();      System.out.println(person + " : " + score);    }  }}/** *  泛型应用于接口上 */class Person implements Comparable<Person> {  private String name;  public Person(String name) {    this.name = name;  }  @Override  public String toString() {    return name;  }  @Override  public int compareTo(Person person) {    if (this == person) {      return 0;   }    return name.compareTo(person.name);  }}

自定义泛型


泛型定义在类、接口或方法上,称为泛型类、泛型接口或泛型方法。

泛型类


语法


    访问修饰符 class 类名 {
        // T 可以当作普通类型来使用
     }

注意:① 不支持泛型数组的声明并开辟长度。
           ② 不支持在静态方法中使用泛型。

错误的用法:

 class MyClass<T> {    T[] t = new T[]; // 错误① 【Cannot create a generic array of T】    T[] t; // 单纯的声明泛型数组可以    public static void method(T t) { // 错误② 【Cannot make a static reference to the non-static type T】    } }

使用


使用泛型类的泛型的时机,是在创建泛型类的对象时
即: 类名<类型> 名 = new 类名<类型>();

示例:

public class Test {  public static void main(String[] args) {    MyClass<String> mc = new MyClass<String>();    mc.get("abc"); // abc  }}class MyClass<T> {  public void get(T t) {    System.out.println(t);  }}

泛型接口


语法


    访问修饰符 interface 接口名 {
        // T 可以当作普通类型来使用
    }

使用


使用泛型接口的泛型的时机,是在实现类实现该泛型接口时、或子接口继承该泛型接口时

即:
    interface MyInterface<T> {
     }

    class MyClass implements MyInterface<类型> { // 实现泛型接口的类

     interface SubInterface extends MyInterface<类型> { // 继承泛型接口的接口
     }

示例:

public class Test {  public static void main(String[] args) {    MyClass mc = new MyClass();    System.out.println(mc.get()); // 123  }}class MyClass implements MyInterface<Integer> {  @Override  public Integer get() {    return 123;  }}interface MyInterface<T> {  T get();}

泛型方法


语法


    修饰符 返回值类型 方法名(E e) {
        // 使用e
     }

使用


使用泛型方法的泛型的时机,是在调用泛型方法时【其实参的类型即为泛型方法的泛型的确定类型】
泛型方法可以用在普通类或普通接口中,也可以用在泛型类或泛型接口中。

即:
    class MyClass {
        public <E> void method(E e) {
            System.out.println(e);
       }
    }

    interface MyInterface<T> {
        T getType();

        <E> void getElement(E e);
    }

示例:

public class Test {  public static void main(String[] args) {    MyClass mc = new MyClass();    mc.set(3.14f); // 3.14  }}class MyClass implements MyInterface {  @Override  public <E> void set(E e) {    System.out.println(e);  }}interface MyInterface {  <E> void set(E e);}

继承和泛型的关系


泛型没有所谓的继承
错误的示范: List list = new ArrayList();

通配符


在方法的形参上使用,为了弥补泛型机制带来的参数传递问题。

无界通配


?
代表任意类型
使用此作为形参限制的方法,只能查找,不能添加【null除外】。

子类限定


? extends 父类型
规定了类型的上限。支持父类型及其子类型。
使用此作为形参限制的方法,只能查找,不能添加【null除外】。

父类限定


? super 子类型
规定了类型的下限。支持子类型及其父类型。
使用此作为形参限制的方法,可以查找,同时只能添加子类型或null,其他类型不能。

示例:

import java.util.ArrayList;import java.util.List;public class Test {  private static void m1(List<?> list) {  // list.add("a"); 【报错】  // list.add(1); 【报错】  list.add(null); // 只能放入null  for (int i = 0; i < list.size(); i++) { // 可以查    System.out.println(list.get(i));  }}private static void m2(List<? extends Animal> list) {  // list.add("a"); 【报错】  // list.add(1); 【报错】  list.add(null); // 只能放入null  for (int i = 0; i < list.size(); i++) { // 可以查    System.out.println(list.get(i));  }}private static void m3(List<? super Animal> list) {  // list.add("a"); 【报错】  // list.add(1); 【报错】  list.add(null); // 可以放入null  list.add(new Animal()); // 可以放入子类型  for (int i = 0; i < list.size(); i++) { // 可以查    System.out.println(list.get(i));  }}public static void main(String[] args) {  List<Animal> list1 = new ArrayList<Animal>();  List<Dog> list2 = new ArrayList<Dog>();  List<Object> list3 = new ArrayList<Object>();  // 可以放入任意类型  m1(list1);  m1(list2);  m1(list3);  // 只能放入Animal及其子类  m2(list1);  m2(list2);  // m2(list3); 【报错】  // 只能放入Animal及其父类  m3(list1);  // m3(list2); 【报错】  m3(list3);  }}class Animal {}class Dog extends Animal {}
1 0