JavaSE 13 集合与泛型,javase13集合
JavaSE 13 集合与泛型,javase13集合
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); // 可以放null
ArrayList 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); // 没有使用泛型,得到的集合元素的类型是Object
System.out.println("角标为0的元素:" + obj);
// 是否包含某元素
boolean contain = al.contains(true);
System.out.println(contain);
// 查看当前集合中元素的个数
System.out.println(al.size());
// 判断当前集合中元素个数是否为0
System.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 {
}
相关文章
- 暂无相关文章
用户点评