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 {}
- JavaSE 13 集合与泛型
- JavaSE(9):java集合与泛型
- javase重新开始系列之集合与泛型
- JAVASE之泛型,集合
- JavaSE学习 第十一章 集合与映射
- JavaSE入门学习40:Java集合框架之泛型
- JavaSE入门学习40:Java集合框架之泛型
- javaSe-集合
- javase集合
- javaSE之多态参数与泛型
- javaSE(11)(集合迭代器与比较器)
- javaSE复习系列之集合篇-HashMap与Hashtable
- 【JavaSE系列-基础篇6】——数组,集合,泛型宏观把控
- 集合与泛型
- 集合与泛型
- 集合与泛型
- 泛型与集合
- javaSE 集合类学习
- PHPMailer发送邮件出现Permission denied的解决办法
- glew使用问题汇总,看能折腾到多少条
- 欢迎使用CSDN-markdown编辑器
- JdbcTemplate 易被 Java 8 Lambda 带入的坑
- 深入理解Java虚拟机学习笔记——四、Java内存模型与多线程
- JavaSE 13 集合与泛型
- C#上机实验之题目三
- Git 使用整理(二)
- C# 节约里程法实现
- AOP--代理模式,拦截器的简易实现及原理
- cout输出i,&i,*i,pi,*pi,&pi
- 单向链表的相关操作
- [链表]九度OJ-1517:链表中倒数第k个结点
- C++智能指针(一)智能指针的简单介绍