声明
- 学习笔记
- 仅供学习和参考
集合
一 集合概述
1 什么是集合
字面意思:集合就是把具有相同性质的一类东西汇聚成一个整体。在面向对象中,对事物都是以对象的形式体现。为了方便对多个对象的操作,需要存储多个对象的容器,而集合就是存储多个对象最常用的一种方式。
2 集合的主要操作
- 将对象添加到集合中
- 从集合中删除对象
- 从集合中查找对象
- 从集合中修改对象
3 集合与数组的区别
在不确定需要多少个对象时,若使用数组存储,数组长度不好定义;但是集合解决了这个问题。在集合中可以存储任意类型的对象,且集合大小是可变的。
数组 | 集合 | |
---|---|---|
容器 | 是 | 是 |
大小 | 一旦创建,大小固定 | 大小一直可变 |
类型 | 基本类型和引用类型 | 引用类型 |
数据 | 存储的数据类型相同 | 存储任意类型 |
4 集合框架
从各种集合形式中提取公共要素,形成一个集合的解决方案,就是Java的集合框架。Collection包括其子接口的超级接口是Iterable,Collection和Map是并列关系。
- 单列集合
Collection(接口) | ||||||
---|---|---|---|---|---|---|
List(子接口) | Set(子接口) | |||||
ArrayList(实现类) | LinkedList(实现类) | Vector(实现类) | HashSet(实现类) | TreeSet(实现类) | ||
LinkedHastSet(实现类) |
- 双列集合
Map(接口) | ||
---|---|---|
Hashtable(实现类) | HashMap(实现类) | TreeMap(实现类) |
Properties(实现类) | LinkedHashMap(实现类) |
(1) Java集合框架的接口
接口 | 特点 |
---|---|
Collection | 集合的根接口,没有实现类,用于引用子接口的对象。Collection 是一组对象的集合。 |
List | 元素之间是有序的,元素可以重复。通过索引类访问元素,允许在指定位置插入元素。 |
Set | 元素之间无序的,元素不可重复。 |
Map | 存储 Key/Value对 的集合。key 值不允许重复,每个 可以最多只能映射一个值。Map 是一组 Key/Value对 的集合。 |
(2) Java集合框架的实现类
接口 | 实现类 | 特点 |
---|---|---|
ArrayList | 底层使用数组实现,长度默认为0。如果集合元素的个数超出当前数组大小,则创建新的扩容60%的数组,并将原来的数组拷贝过来。由于数组是按内存位置依次存放,因此查找快,但是增删元素涉及到后面的元素移动,因此增删元素要慢。 | |
List | LinkedList | 底层使用链表实现。增删元素时,链表只需要增加或删除一个节点,这样的增删效率较高,但查询时需要一个个的遍历,所以查询效率较低。 |
Vector | 底层使用数组实现。Vector 是线程安全的,所以效率略低。ArrayList 是线程不安全的所以效率较高 | |
Set | HashSet | 底层是以哈希表实现的,所有元素是按hashCode 的值存放。HashSet是线程不安全,存取速度快。 |
TreeSet | 底层使用红-黑树的数据结构实现。要求元素实现Comparable接口,默认对元素进行自然排序(String)。 | |
LinkedHashSet | 底层使用链表技术实现,但是结合了哈希值实现存储。LinkedHashSet的增删效率高,查询效率低。 | |
HashMap | 底层是哈希表存储。HashMap 没有对 key 排序,可以存入null键和null值。HashMap 是线程不安全的。 | |
Map | TreeMap | 底层是红-黑树的数据结构实现。TreeMap 对 key 进行排序,要求 key 实现 Compareable 接口。 |
HashTable | 底层是哈希表数据结构。HashTable 是线程安全的,不可以存入null键和null值。HashTable 效率较低,已被HashMap代替。 | |
LinkedHashMap | 底层使用链表技术实现,但是结合了哈希值实现存储。LinkedHashMap 的增删效率高,插叙效率低。 |
(3) 应用场景
出现这么多集合容器的原因:因为每一个容器对数据的存储方式不同,为各种存储方式提供了多样化的选择(存储+操作)。
接口 | 描述 |
---|---|
Collection | 这是一个纯粹的接口,用于引用 List和Set 的实现对象。 |
LIst | 如果需要保留存储顺序,并且允许保留重复数据,使用 LIst 接口的实现。如果查询较多,那么使用 ArrayList。如果存取较多,那么使用 LInkedList。如果需要线程安全,那么使用Vector。 |
Set | 如果不需要保留存储顺序,并且需要去掉重复元素,使用 Set 接口的实现类。如果需要将元素排序,那么使用TreeSet。如果不需要排序,使用 HashSet。HastSet 比 TreeSet 效率高。如果需要保留存储顺序,又要过滤重复元素,那么使用 LinkedHashSet。 |
Map | 如果需要保留 key/value 形式数据,使用 Map 接口的实现类。如果需要将元素排序,那么使用 TreeMap。如果不需要排序,使用HashTable。HastTable 比 TreeMap 效率高。 |
二 Collection
1 什么是Collention
Colletion 是一个集合接口,为集合对象提供了最大化的同一操作方式。集合类都是在 java.util 包中,因此 Collection 接口就是 java.util.Collection。Collection 没有实现类,通常用于对实现子接口的对象引用。例如:使用 List 的具体实现类 ArrayList 创建一个集合对象:
Collection collection = new ArrayList();// 向上转型
2 基本操作
方法声明 | 方法作用 |
---|---|
add(E e) | 向集合添加元素,参数类型是 Object。成功返回 true,失败返回 false。 |
remove(Object o) | 删除指定元素。成功返回 true,失败返回 false。 |
size() | 获取集合中元素的个数 |
clear() | 清空集合中的所有元素 |
isEmpty() | 判断集合是否为空 |
3 元素包含
方法声明 | 方法作用 |
---|---|
contains(Objec o) | 判断集合中是否包含指定对象 |
注意:
contains 内部是依赖于 equals 方法进行比较的,所有支持 contains 判断的对象必须重写 equals 方法。请注意 Java 规范:重写 equals 方法时重写 hashCode 方法。例如:Person类 支持 contains 操作,就需要重写 equals 方法。
import java.util.Objects;
public class Person {
private int id;
private String name;
public Person() {
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
import java.util.ArrayList;
import java.util.Collection;
public class CollectionContainsDemo {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(new Person(1,"李"));
collection.add(new Person(2,"王"));
collection.add(new Person(3,"张"));
System.out.println(collection.contains(new Person(1,"李")));
System.out.println(collection.contains(new Person(3,"zhang")));
}
}
4 集合操作
(1)集合关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lVNCuUe9-1676430750905)(G:\Java-栈\课程笔记\集合\集合.PNG)]
假定上面有 三个集合:A ,B和C,其中A的范围1和3,B的范围3和4,C的范围2。
现在有以下关系:
子集:集合C 是集合A的子集,因为C的所元素都在A中存在。
并集:集合A和集合B的并集,范围是1,3,4,即二者的所有的元素。
交集:集合A和集合B的交际,范围是3,即二者共有的所有元素。
补集:集合B相对于集合A的补集,范围是4,即在集合B中,除去集合A和B的交集,剩下的所有元素称为B对A的补集。
(2)集合实现
方法 | 集合关系 | 方法描述 | equals用否 |
---|---|---|---|
c.addAll(Collection c1) | 并集 | 把c1的元素添加到c集合中去 | 否 |
c.removeAll(Collection c1) | 补集 | 删除c集合中与c1的交集元素 | 用 |
c.retain(Collection c1) | 交集 | 保留c集合与c1的交集元素,其他的元素一并删除 | 用 |
c.containsAll(Collection<?> c1) | 子集 | 判断集合中是否包含指定集合。和 contains 方法一样,也是使用equals 方法判断两个对象是否相等 | 用 |
public class CollectionContainsDemo {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(new Person(1,"李"));
collection.add(new Person(2,"王"));
collection.add(new Person(3,"张"));
Collection collection1 = new ArrayList();
collection1.add(new Person("李"));
collection1.add(new Person("王"));
collection1.add(new Person("zhang"));
System.out.println("-----------并集");
System.out.println(collection.addAll(collection1));
System.out.println("并集:"+collection);
System.out.println("-----------补集");
System.out.println(collection.remove(collection1));
System.out.println("补集:"+collection);
System.out.println("-----------交集");
System.out.println(collection.retainAll(collection1));
System.out.println("交集:"+collection);
System.out.println("------------子集");
System.out.println(collection.contains(collection1));
}
}
5 集合转数组
方法声明 | 方法描述 |
---|---|
Object[] toArray() | 集合转成数组。只能使用 Object[] 作为返回类型 |
toArray(T[]) | 集合转成数组。事先指定数组的类型,可以避免强制转型 |
public class CollectionDemo02 {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(new Person(1,"李"));
collection.add(new Person(2,"王"));
collection.add(new Person(3,"张"));
Object[] objects = collection.toArray();
System.out.println(Arrays.toString(objects));
Person[] people = new Person[collection.size()];
Object[] objects1 = collection.toArray(people);
System.out.println(Arrays.toString(objects1));
}
}
// 结果
[Person{id=1, name='李'}, Person{id=2, name='王'}, Person{id=3, name='张'}]
[Person{id=1, name='李'}, Person{id=2, name='王'}, Person{id=3, name='张'}]
Process finished with exit code 0
6 迭代器
Java 实现了迭代器 Iterator 对象,它提供一些方法实现了对集合中元素的遍历。Collection 接口定义了获取迭代器的方法(iterator()),所有的Collection类型的集合都可以通过这个方法获取去自身的迭代器。
(1)Iterator< E > 接口
该接口就是迭代器接口,定义了常见的迭代方法:
方法声明 | 方法描述 |
---|---|
boolean hashNext() | 判断集合中是否有下一个元素,如果有元素就返回true,没有返回false。 |
E next() | 返回集合中的下一个元素,注意:如果没有下一个元素时,调用 next 元素会抛出 NoSuchElementException |
void remove() | 从集合中移除迭代器返回的最后一个元素(可选操作) |
注意:
每个迭代器中都有一个隐含的位置指针,从集合中获得迭代器时,这个位置执政只想第一个元素之前,此时直接获取元素时将抛出异常,必须调用 next() 将位置指针移到第一个元素的位置上才可以正确的访问。
最开始指针指在第一行之前,迭代前要调用next()将指针移到下一行(或者也叫游标) | |
---|---|
0 | |
1 | |
2 | |
3 | |
4 | |
… |
(2)迭代器遍历相关操作
while 循环
public class Demo04 {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("张三");
collection.add("李四");
collection.add("王五");
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
// 结果
张三
李四
王五
Process finished with exit code 0
for 循环
一般不使用这种形式
public class Demo05 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("张三");
collection.add("李四");
collection.add("王五");
for ( Iterator<String> iterator = collection.iterator();iterator.hasNext();) {
String next = iterator.next();
System.out.println(next);
}
}
}
// 结果
张三
李四
王五
Process finished with exit code 0
使用迭代器清空集合
一般不使用这种操作方式
public class Demo06 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("张三");
collection.add("李四");
collection.add("王五");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();// 删除当前迭代的元素
}
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
}
}
// 结果
Process finished with exit code 0
注意:
- 如果迭代器已经只想集合的末尾,如果在调用 next()返回 NoSuchElementException异常 。
- 在调用remove之前没有调用next是不合法的,会抛出 IllegalStateException 异常。
不安全操作
在对集合进行迭代过程中,不允许出现元素的非迭代器操作,因为这样会产生安全隐患。在集合遍历中,使用迭代器操作集合对象时,又使用结合其他 API 操作集合时,将会抛出并发修改异常(ConcurrentModificationException)。
public class Demo06 {
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("张三");
collection.add("李四");
collection.add("王五");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
collection.add("a");
}
}
}
// 结果
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.etime04.Demo06.main(Demo06.java:16)
Process finished with exit code 1
三 List
1 什么是List
List 是一个有序的集合,它按元素加入到集合中的顺序依次摆放。注意:不是按集合中对象内的属性值排序,这里是按插入位置的顺序。
0 | 1 | 2 | 3 | 4 | 5 | 6 | … | 位置 |
---|
List的特点:
- 元素有序
- 元素有下标
- 元素可重复
- 遍历时存入顺序和取出顺序一致
2 特有方法
由于List集合中元素是有序的,它的所有实现类都支持索引,这些方法都是和索引相关。
操作 | 方法声明 | 方法描述 |
---|---|---|
Boolean addAll(int index,Collection c) | 指定位置添元素 | |
增加 | void add(int index,E element) | 指定位置添元素 |
删除 | E remove(int index) | 删除指定位置元素 |
修改 | E set(int index,E element) | 返回的是需要替换的集合中的元素 |
E get(int index) | 注意:IndexOutBoundsExcption | |
int indexOf(Object 0) | 找不到返回-1 | |
int lastIndexOf(Object o) | 找不到返回-1 | |
获取 | List subList(int fromIndex,int toIndex) | 求子集和:[fromIndex,toIndex) |
迭代 | listIterator() |
注意:
在JavaSE中 List 名称的类型有两个,一个是 java.util.List 集合接口,一个是 java.awt.List 图形界面的组件,别导错包。
(1)常用功能
import java.util.ArrayList;
import java.util.List;
public class Demo04 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();// 接口的多态 向上转型 父接口与子接口
list.add("li");
list.add("zhang");
list.add("wang");
System.out.println(list);
list.add(1,"zhao");// 指定位置 添加元素
System.out.println(list);
System.out.println("---------------------");
List<String> list1 = new ArrayList<>();
list1.add("小李");
list1.add("小张");
list1.add("小王");
list.addAll(1,list1);
System.out.println(list);// 把list1的元素添加到 list中下标为1(包括1)的指定位置
System.out.println("---------------------");
// 根据索引获取值获取元素
System.out.println("get:"+list.get(0));
// 使用get 遍历集合
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i)+",");
}
System.out.println("---------------------");
// 替换指定位置的元素
String str = list.set(0, "李");// 返回替换指定位置的元素
System.out.println(str);
System.out.println(list);
System.out.println("---------------------");
// 删除指定位置的元素
String remove = list.remove(0);// 返回删除指定位置的元素
System.out.println(remove);
System.out.println(list);
System.out.println("---------------------");
// 第一次出现的索引
int first = list.indexOf("小张");
System.out.println("first="+first);
list.add("小张");
int last = list.lastIndexOf("小张");
System.out.println("last="+last);
System.out.println("---------------------");
List<String> list2 = list.subList(1, 3);// 范围 [ )
System.out.println(list);
System.out.println(list2);
}
}
// 结果
[li, zhang, wang]
[li, zhao, zhang, wang]
---------------------
[li, 小李, 小张, 小王, zhao, zhang, wang]
---------------------
get:li
li,小李,小张,小王,zhao,zhang,wang,---------------------
li
[李, 小李, 小张, 小王, zhao, zhang, wang]
---------------------
李
[小李, 小张, 小王, zhao, zhang, wang]
---------------------
first=1
last=6
---------------------
[小李, 小张, 小王, zhao, zhang, wang, 小张]
[小张, 小王]
Process finished with exit code 0
(2 ) ListIterator
ListIterator 是List集合的特有迭代器,支持迭代过程中添加元素和修改元素
方法声明 | 方法描述 |
---|---|
hasPrevious() | 判断是否存在上一个元素 |
previous() | 当前指针先向上移动一个单位,然后再取出当前指针指向的元素 |
next() | 先取出当前指针指向的元素,然后指针向下移动一个单位 |
add(E e) | 把当前有元素插入到当前指针指向的位置上 |
set(E e) | 替换迭代器 最后依次返回的元素 |
(3)集合遍历
3 ArrayList
再实际的开发中,ArrayList 是使用频率最高的一个集合实现类。
(1)原理
ArrayLIst 底层维护了一个Object[]用户存储对象,默认数组的长度 为10。当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来容量的1.5倍。
由于底层使用数组实现,在增加和删除的时候会牵扯到数组增容和拷贝元素,所以效率比较慢。但是数组可以直接按索引查找元素,所以查找时较快。
例如:假设向数组的 0 下标位置添加元素,那么原来的下标位置的元素与需要整体往后移动,并且数组可能还要增容。一旦增容,就需要将老数组的内容拷贝到新数组中。所以数组的增删的效率时很低的。
public class Demo01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
System.out.println(arrayList.size());
System.out.println(arrayList.isEmpty());
System.out.println("--------------");
// 添加
arrayList.add("乔丹");
arrayList.add("勒布朗");
arrayList.add("罗斯");
System.out.println(arrayList.isEmpty());
// 获取
System.out.println("--------------");
System.out.println(arrayList.get(1));
System.out.println("--------------");
// 遍历
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
System.out.println("--------------");
// 删除
Object remove = arrayList.remove(0);
System.out.println(remove);
System.out.println(arrayList.size());
System.out.println("--------------");
// 清空所有
arrayList.clear();
System.out.println(arrayList.isEmpty());
System.out.println(arrayList.size());
}
}
// 结果
0
true
--------------
false
--------------
勒布朗
--------------
乔丹
勒布朗
罗斯
--------------
乔丹
2
--------------
true
0
Process finished with exit code 0
(2) 例子
import java.util.ArrayList;
public class Demo03 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("li");
arrayList.add("zhang");
arrayList.add("zhang");
System.out.println(arrayList);
// 取出arraylist集合中重复的元素
ArrayList<String> arrayList1 = new ArrayList();
for (int i = 0; i < arrayList.size(); i++) {
String string = arrayList.get(i);
if (!(arrayList1.contains(string))) { //包含
arrayList1.add(string);
}
}
System.out.println(arrayList1);
}
}
// 结果
[li, zhang, zhang]
[li, zhang]
Process finished with exit code 0
(3) 指定类型
-
<> 泛型----> 广泛(规范) 类型
-
写法–入门
import java.util.ArrayList;
// 泛型
public class Demo02 {
public static void main(String[] args) {
// 写法1 --指定为Person的对象
ArrayList<Person> arrayList = new ArrayList<>();
// 写法2
ArrayList<Person> arrayList1 = new ArrayList<Person>();
arrayList.add(new Person("li",22));
arrayList.add(new Person("zhang",21));
//arrayList.add("st"); String类型与指定类型不符
}
}
注意:
泛型中(<>),其中指定了类型,便不能添加其他类型的数据了。
4 LinkedList
(1)原理
由于 LinkedList 在底层使用链表实现存储结构,需要让上一个元素记住下一个元素,所以每个元素中保存的有下一个元素的位置。虽然 LinkedList 中也有下标,但是查找的时候需要从头往下找,显然时没有数组查找块。但是,链表在插入新元素的时候,值需要让前一个元素记住新元素,让心愿记住下一个元素就可以了,所以插入很快。
(2) 特有方法
方法 | 方法描述 |
---|---|
addFirst(E e) | 增加到集合的最前面 |
addLast(E e) | 增加到集合的最后面 |
getFirst() | 获得集合的第一个元素 |
getLast() | 获得集合的最后一个元素 |
removeFirst() | 如果集合中没有原申诉,获取或者删除元素时抛出 NoSuchElementException |
removeLast() | 如果集合中没有原申诉,获取或者删除元素时抛出 NoSuchElementException |
descendingllterator() | 返回逆序的迭代器对象 |
5 Vector
Vector 时一个线程安全的集合。它和 ArrayList 特性相似,较少很用。
(1) 特有的方法
方法声明 | 方法作用 |
---|---|
void addElement(E obj) | 在集合末尾添加元素 |
E elementAt(int index) | 返回指定角标的元素 |
Enumeration element() | 返回集合 中的所有元素,封装到Enumeration 对象中 |
(2)Enumeration 接口
方法声明 | 方法作用 |
---|---|
boolean hashMoreElements() | 测试此枚举是否包含更多的元素 |
E nextElement() | 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素 |
四 泛型
1 问题引入
在 JDK1.5之前,对象保存到集合中就会失去自身类型信息,从集合中取出元素时需要进行类型的强制转换,这样不可避免就会引发产需的一些安全性问题。
(1)例子
当集合中存储不同的对象类型时,那么在运行时可能对导致强制类型异常(迭代)
(2)原因分析
在集合中可以存储热议对象,对象保存到集合中都将被认为是 Object 类型。如果需要使用对象的特有方法,那么就需要类型转换,但是集合中存入的对象可能是不同的,这将引发类型转换异常。
(3)解决办法
在类型转换的时候,虽然可以通过 if 语句进行类型检查 (instanceof) ,但是效率较低。我们可以通过个容器加限定类型的方式,规定容器只能存储一种类型的对象,就像给容器贴标签说明该容器中只能存储说明杨类型的对象一样。所以在 JDK1.5中出现泛型。
2 泛型应用
(1)泛型语法
以ArrayList< E >为例,其中<>读成 typeof.
- ArrayList< E >中的E称为类型参数,整个称为 ArrayList< E > 泛型类型。
- ArrayList< String >中的 String 称为实际类型参数,整个ArrayList< String >称为参数化的类型。// 检查作用:是否为String类型
(2)典型应用
在下面例子中使用泛型后,将运行时发生的异常提前至编译时被发现,同时在获取元素的时候无需强制转换,避免了类型转换的异常问题。
A 支持泛型的集合中只能是指定类型元素
B 泛型类型必须是引用类型
泛型类型必须是引用类型,不支持基本类型。如果需要支持基本类型,那么使用基本类型的包装类,如int使用Integer类。
C 集合的方法支持泛型
在集合的实现类中,都对泛型提供了较好的支持,使用泛型后取出元素不与要类型转换。
(3)擦除泛型
泛型通常不会直接用于创建对象,而是使用这个泛型类定义一个具有类型的类。在包含泛型的Java类被编译后,生成的Class文件中使用具体类型替换泛型 (T) 敌营,将会定义一个完整的类,这个过程称之为"擦除"。
3 泛型方法
需求:写一个函数,调用者传递什么类型的变量,该函数就返回什么类型的变量?
(1) 无泛型方案
无法确定具体传递什么类型的数据,那么方法的形参就只能定义为Object类型,返回值也是Object类型的。
public class Demo02 {
public static void main(String[] args) {
// 使用方法,因为参数是引用传递,所以基本数据类型自动装箱为Integer(包装类)对象
Integer number = (Integer) getNumber(1);
// 在使用number时,自动拆箱为基本数据类型
System.out.println("number="+number);
}
public static Object getNumber(Object object){
return object;
}
}
// 结果
number=1
Process finished with exit code 0
(2) 泛型方法
public class Demo01 {
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
System.out.println(demo01.getDate(1));
System.out.println(demo01.getDate("li"));
System.out.println(demo01.getDate(new Object()));
}
// static不能定义泛型方法
public<T> T getDate(T t){
return t;
}
}
// 结果
1
li
java.lang.Object@1540e19d
Process finished with exit code 0
(3) 泛型方法实例
4 泛型类
如果一个类中多处都要用到同一个泛型,这是可以把泛型定义在类上(即类级别的泛型)
(1) 泛型类
public class Demo02<T> {
public static void main(String[] args) {
}
public void pringArray(T[] array){
System.out.println(Arrays.toString(array));
}
}
5 泛型接口
(1) 泛型接口
public interface MyInterface<T> {
void print(T t);
}
public class MyInterfaceImpl<T> implements MyInterface<T>{
@Override
public void print(T t) {
}
}
(2) 泛型接口实例
6 泛型的限定
<? extends E> 向下限定,E及其子类 <? super E> 向上限定,E及其父类 <?>任意类型public class Animal {
}
public class Cat extends Animal {
}
public class Dog extends Animal {
}
public class Demo03 {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
List<Cat> cats = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
System.out.println("向下限定");
print(animals);
print(cats);
print(dogs);
System.out.println("向上限定");
List<Object> objects = new ArrayList<>();
print2(animals);
print2(objects);
System.out.println("任意类型");
print3(animals);
print3(cats);
System.out.println(objects);
}
public static void print(Collection<? extends Animal> collection){
System.out.println("哈哈");
}
public static void print2(Collection<? super Animal> collection){
System.out.println("哈哈2");
}
public static void print3(Collection<?> collection){
System.out.println("哈哈3");
}
}
//
向下限定
哈哈
哈哈
哈哈
向上限定
哈哈2
哈哈2
任意类型
哈哈3
哈哈3
[]
五 Set
1 Set
Set 中元素是无序 (无下标) 存放的,即元素存入和取出的不一定相同,并且元素不能重复。其中,HashSet 是一个常用的Set。
Set的特点:
- Set 中的元素无序,但可以排序。
- Set 中的元素无下标。
- Set 中的元素不可重复。
(1)存储原理
和 List 不同的是,Set 中的对象不是按元素加入到集合中的先后顺序依次摆放,因此 Set 中是没有索引的,也不能按索引访问集合中的对象。
在 Set 内部的存储结构使用了散列算法 (HASH算法),按对象的散列值决定存放位置,也按照散列检索对象。由于散列值的无序特点,依次 Set 的行为表现为一个无序的集合。
hashCode | hashCode | hashCode | hashCode | … |
---|
例如:有三个创建好的对象要加入到一个Set 中
public class Demo07 {
public static void main(String[] args) {
Object object1 = new Object();
Object object2 = new Object();
Object object3 = new Object();
}
}
现在分析 Set 中存放对象的内部行为:
-
Set set = new HashSet();
-
最开始是一个空集合
-
-
-
set.add(object1);
-
现加入object1
-
object1 的hashCode值决定的位置
-
object1
-
-
set.add(object2);
-
现加入object2
-
object2 的hashCode值决定的位置
-
object2 object1
-
核心小结:
- HashSet 调用对象的 hashCode() 方法得到对象的 hashCode 值,并依据 hashCode 值决定该对象在 HashSet 中存放的位置。
- HashSet 判断两个对象是否相等的依据:两个对象通过 equals() 方法比较后结果为相等,并且两个对象的hashCOde() 方法得到对象的 hashCode值 也相等
- 规则:如果两个对象通过 equals() 方法比较后结果为相等,那么两个对象的hashCode() 方法得到对象的hashCode() 值也应该是相等的
- 建议:利用 IDE重写 hachCode() 和 equals() 方法
(2)常用方法
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo06 {
public static void main(String[] args) {
Set<Person> set = new HashSet<>();
Person p1 = new Person("lifa",20);
Person p2 = new Person("la",20);
Person p3 = new Person("lia",20);
Person p4 = new Person("liaf",20);
System.out.println("-------------添加");
set.add(p1);
set.add(p2);
System.out.println(set);
System.out.println("-------------删除");
set.remove(p2);
System.out.println(set);
System.out.println("-------------遍历");
set.add(p3);
set.add(p4);
Iterator<Person> iterator = set.iterator();
while (iterator.hasNext()) {
Person next = iterator.next();
System.out.println(next);
}
System.out.println("--------------------");
System.out.println(set.size());
System.out.println(set.isEmpty());
System.out.println("----------------");
// 重复的前提 泛型中的类 要重写equals 和 hashCode 方法
Person p5 = new Person("li",20);
Person p6 = new Person("li",20);
set.add(p6);
set.add(p5);
System.out.println(set);
}
}
// 结果
-------------添加
[Person{name='la', age=20}, Person{name='lifa', age=20}]
-------------删除
[Person{name='lifa', age=20}]
-------------遍历
Person{name='liaf', age=20}
Person{name='lia', age=20}
Person{name='lifa', age=20}
--------------------
3
false
----------------
[Person{name='liaf', age=20}, Person{name='lia', age=20}, Person{name='li', age=20}, Person{name='lifa', age=20}]
Process finished with exit code 0
(3)特有方法
该集合中没有特有的方法,直接继承 Collection 的方法。
(5)重复元素
再Java中,重复元素使用 Object类中的 hashCode 和 equals 判断。在类中重写了 hashCode 和 equals,当两个的 hashCode 方法返回相同值斌且两个对象的equals 返回true时,两个对象时相同的。
有一些创建的重复元素情形:
- 所有 null 常量都是重复元素
- 相同对象的不同引用变量是重复元素
- String 对象以重写 hashCode 和 euqals() ; 即只要两个String 对象通过 equals() 方法比较后结果为相等,那么这两个String对象的hashCode()等到的哈市Code值也是相等的。简单说:两个String对象的值相同时,就认为这两个String对象是重复的。
import java.util.HashSet;
import java.util.Set;
public class Demo11 {
public static void main(String[] args) {
Set set = new HashSet();// 没有指定类型
set.add(null);
set.add(null);// null 对象不能重复加入,这是替换--自动封箱
System.out.println(set.size());
// 同一个对象的多个引用变量
Person person = new Person("li",20);
Person person1 = person;
set.add(person);
set.add(person1);
System.out.println(set);
// 相同值的String对象
String string = "hello";
String string1 = new String("hello");
System.out.println((string==string1));
System.out.println(string.equals(string1));
set.add(string);
set.add(string1);
System.out.println(set);
}
}
// 结果
1
[null, Person{name='li', age=20}]
false
true
[null, Person{name='li', age=20}, hello]
Process finished with exit code 0
2 HashSet
使用 Set 集合都是需要去掉重复元素的。如果在存储的时候逐个 equals() 比较,效率较低,哈希算法提高了去重复的小笼包,降低了使用equals() 方法的次数。当 HashSet 调用 add() 方法存储对象的时候,先调用对象的 hashCode() 方法得到一个哈希值,然后再集合中查找是否由哈希值相同的对象,如果没有韩析置相同的对象就直接村粗集合,如果有哈希值相同的对象,就和哈希值相同的对象诸葛进行equals() 比较,比较结果为false 就存入,true 则不存入。
3 TreeSet
TreeSet 是 SortedSet 接口的唯一实现类,TreeSet 可以确保集合元素处于排序状态。TreeSet 支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。
(1)引入
设计以个数据结构用于保存数据,要求不能重复存储元素,并且对数据支持排序。
ArrayLIst,LinkedList 不能去除重复数据。
HashSet 可以去除重复,但是不支持排序。
这里引入一个新的数据结构,TreeSet。
(2)排序接口
在 Java 中提供了一个对象比较接口 Comparable,它只用一个方法:
方法声明 | 方法描述 |
---|---|
int comparableTo(Object o) | 大于返回正数,等于返回零,小于返回负数 |
所有加入 TreeSet 的元素必须实现 Comparable 接口,否则会抛出异常,在TreeSet 内部使用 Comparable 接口对元素排序。和 HashSet 相比,它的效率较低。
(3) 实例
String本身实现了 Comparable 接口。
实现对Person排序,如果年龄相同就再比较姓名。
import java.util.Objects;
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
if (this.getAge() > o.getAge()) {
System.out.println(1);
return 1;
} else if (this.getAge() < o.getAge()) {
System.out.println(2);
return -1;
}else {
System.out.println(3);
return this.getName().compareTo(o.getName());
}
}
}
public class Demo08 {
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("bac");
set.add("csc");
set.add("fcd");
set.add("ays");
set.add("bac");
System.out.println("size="+set.size());
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("---------------");
Set<Person> set1 = new TreeSet<>();
Person p1 = new Person("lifa",20);
Person p2 = new Person("la",21);
Person p3 = new Person("lia",20);
Person p4 = new Person("lia",20);
set1.add(p1);
set1.add(p2);
set1.add(p3);
set1.add(p4);
// System.out.println(set1);
Iterator<Person> iterator1 = set1.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
}
}
// 结果
size=4
ays
bac
csc
fcd
---------------
3
1
3
3
3
Person{name='lia', age=20}
Person{name='lifa', age=20}
Person{name='la', age=21}
Process finished with exit code 0
4 LinkedHashSet
LinkedHashSet 是具有可与之迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,后者维护这一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。
六 Map
在集合框架中,Map 与 Collection 是并列存在的,互相没有继承关系。
Map 的特点:
- 采用key/values 的形式存储数据
- Map 中key不可重复
- 各个键值对无序
- 各个键值对无下标
1 Map
(1)数据结构
映射可以看到一张多行两列的表格,每一列是"key",第二列是"values"。例如,使用Map集合管理人员信息:
(2)基本操作
Map 是一种按照键(key)存储元素的容器,键(key) 可以是任意类型的对象,所有 value 按 key 存储和获取。
方法声明 | 方法描述 |
---|---|
V put(K key,V value) | 按 key增加value。如果在 Map 中已经有相同的 key,则返回以前的对象,并使用 value 覆盖原来的对象。 |
putAll(Map<? extend K,? extends V> m) | 将参数m中的所有key/value对加入到集合中 |
remove(key) | 删除key对应的 key/value对 |
value get(key) | 按key 获取 value。如果没有匹配的key则返回null |
clear() | 清空集合对象 |
boolean isEmpty() | 长度为0返回true,否则返回false |
int size() | 返回 Map 中key/value 对的个数 |
boolean containsKey(Object key) | 判断集合中是否包含指定的key |
boolean containsValue(Object value) | 判断集合中是否包含指定的value |
mport java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo09 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("中国","北京");
map.put("俄罗斯","莫斯科");
map.put("美国","纽约");
// 不允许键重复
map.put("美国","白宫");
System.out.println(map);
System.out.println("------------");
// 依据键获取值
String string = map.get("中国");
System.out.println(string);
System.out.println("------------");
// 包含 contians
System.out.println(map.containsKey("中国"));
System.out.println(map.containsValue("北京"));
System.out.println("------------");
// keySet() 获取键的Set
Set<String> strings = map.keySet();
System.out.println(strings);
System.out.println("------------");
// 迭代
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
String string1 = map.get(next);
System.out.println(next+" "+string1);
}
System.out.println("------------");
// 删除 依据键删除
String re = map.remove("美国");
System.out.println(re);
System.out.println(map);
}
}
// 结果
{美国=白宫, 俄罗斯=莫斯科, 中国=北京}
------------
北京
------------
true
true
------------
[美国, 俄罗斯, 中国]
------------
美国 白宫
俄罗斯 莫斯科
中国 北京
------------
白宫
{俄罗斯=莫斯科, 中国=北京}
Process finished with exit code 0
(3) 遍历操作
方法声明 | 方法描述 |
---|---|
Set< K > keySet() | 获得所有 key 组成Set集合 |
Collection< V > values() | 获取所有 value 组成的集合 |
Set< Map.Entry< K,V > > entrySet() | 获取所有 Map.Entry 组成的集合。Map.Entry 由 key,value 组成,通过 getKey,getValue获取其键和值 |
使用 keySet()
这是最常用的Map迭代实现方式。
将key转成Set集合,通过迭代器取出Set集合中的每一个key,再通过key获取对应的value。
public class Demo09 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("中国","北京");
map.put("俄罗斯","莫斯科");
map.put("美国","纽约");
// 不允许键重复
map.put("美国","白宫");
System.out.println(map);
// keySet() 获取键的Set
Set<String> strings = map.keySet();
System.out.println(strings);
Iterator<String> iterator3 = strings.iterator();
while (iterator3.hasNext()) {
String key = iterator3.next();
System.out.println(map.get(key));
}
}
}
使用 values()
将 value 转成Set集合,通过迭代器取出Set集合中的每一个value
public class Demo09 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("中国","北京");
map.put("俄罗斯","莫斯科");
map.put("美国","纽约");
// 不允许键重复
map.put("美国","白宫");
System.out.println(map);
// values() 迭代器遍历
Collection<String> values = map.values();
System.out.println(values );
Iterator<String> iterator1 = values.iterator();
while (iterator1.hasNext()) {
String next = iterator1.next();
System.out.println(next);
}
}
}
使用 Map.Entry 对象
这种方式一般不使用
将 key,value 转成Set集合(entrySet()),通过迭代器取出Set集合中的每一个 entrySet
public class Demo09 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("中国","北京");
map.put("俄罗斯","莫斯科");
map.put("美国","纽约");
// 不允许键重复
map.put("美国","白宫");
System.out.println(map);
// entrySet() 获得所有记录,条目
// 方式1
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Iterator<Map.Entry<String, String>> iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Map.Entry<String, String> next = iterator2.next();
System.out.println(next);
}
// 方式2
while (iterator2.hasNext()) {
Map.Entry<String, String> entry1 = iterator2.next();
String key = entry1.getKey();
String value = entry1.getValue();
System.out.println(key+","+value);
}
}
}
2 HashMap
HashMap 是是哟个最广泛的 Map 实现,它的底层是哈希表数据结果实现存储,不支持多线程同步,可以存入null键,null值。
import java.util.HashMap;
public class Demo10 {
public static void main(String[] args) {
HashMap<Person,String> hashMap = new HashMap<>();
hashMap.put(new Person("li",20),null);
hashMap.put(new Person("zhang",21),"北京");
System.out.println(hashMap);
}
}
// 结果
{Person{name='li', age=20}=null, Person{name='zhang', age=21}=北京}
Process finished with exit code 0
3 LinkedHashMap
LinkedHashMap 底层使用链表实现。
4 TreeMap
HashMap 中的所有数据都是无序存放的,在TreeMap中按照key 进行排序。要求key必须实现Comparable接口,具体要求见TreeMap中的实现要求。
七 工具类
1 Collections
Collections 不是 Collection 接口的实现类,它是一个工具类,其中所有方法都是静态的。
注意: Collections he Collection 的区别
(1)List 集合操作
只适用于 List 集合的工具方法。
功能 | 方法 | 方法描述 |
---|---|---|
二分查找 | int binarySearch(list,key) | 集合中元素都是 Comparable 的实现类,且元素要按升序排列 |
排序 | sort(list) | 集合中元素都是 Comparable 的实现类 |
顺序反转 | reverse(list) | 集合中元素都是 Comparable 的实现类 |
强行逆转 | Comparator reverseOrder(Comparator) | 按 Comparator 比较方式逆转 |
位置交换 | swap(list,x,y) | 接索引文职交换 |
元素替换 | replaceAll(list,old,new) | 如果替换的元素不存在,那么原集合不变 |
(2)其他工具方法
功能 | 方法 | 方法描述 |
---|---|---|
最大值 | max(Collection) 或者 max(Collection,comparator) | |
最小值 | min(Collection) 或者 min(Collection,comparator) | |
同步集合 | Set synchronizedSet(Set< T > s) ,Map synchronizedMap(Map< K,V > m),List synchronizedList(List< T > list) | 将不同步的集合变成同步的集合 |
(3)实例
import java.util.*;
public class Demo01 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(5);
list.add(0);
list.add(8);
list.add(1);
list.add(2);
System.out.println("-----------------------排序");
Collections.sort(list);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
System.out.println(next);
}
System.out.println("-----------------------集合转数组");
Object[] objects = list.toArray();
System.out.println(Arrays.toString(objects));
System.out.println("-----------------------数组转集合");
Integer[] arr = new Integer[]{2,5,8};
List<Integer> list3 = Arrays.asList(arr);
System.out.println(list3);
list.addAll(list3);
System.out.println(list);
System.out.println("-----------------------二分查找");
int index = Collections.binarySearch(list, 8);
System.out.println("index="+index);
int index2 = Collections.binarySearch(list, 6);
System.out.println("index2="+index2);
System.out.println("-----------------------顺序反转1");
Collections.reverse(list);
for (Integer i:list
) {
System.out.println(i);
}
System.out.println("-----------------------顺序反转2");
System.out.println("-----------------------位置交换");
Collections.swap(list,0,1);
System.out.println(list);
System.out.println("-----------------------元素替换");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(3);
list2.add(4);
list2.add(7);
Collections.sort(list2);
Collections.replaceAll(list,0,9);// 只能替换引用类型
System.out.println(list);
System.out.println("-----------------------最大值");
Integer max = Collections.max(list);// 装箱
System.out.println("max="+max);// 拆箱
System.out.println("-----------------------最小值");
Integer min = Collections.min(list);
System.out.println("min="+min);
System.out.println("-----------------------使用指定元素替换集合中的所有元素");
Collections.fill(list,3);
System.out.println(list);
}
}
// 结果
-----------------------排序
0
1
2
5
8
-----------------------集合转数组
[0, 1, 2, 5, 8]
-----------------------数组转集合
[2, 5, 8]
[0, 1, 2, 5, 8, 2, 5, 8]
-----------------------二分查找
index=7
index2=-8
-----------------------顺序反转1
8
5
2
8
5
2
1
0
-----------------------顺序反转2
-----------------------位置交换
[5, 8, 2, 8, 5, 2, 1, 0]
-----------------------元素替换
[5, 8, 2, 8, 5, 2, 1, 9]
-----------------------最大值
max=9
-----------------------最小值
min=1
-----------------------使用指定元素替换集合中的所有元素
[3, 3, 3, 3, 3, 3, 3, 3]
Process finished with exit code 0