7.1 Collection集合
7.1.1 集合概述
- 集合:集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组的区别:
1. 数组的长度是固定的。集合的长度是可变的。
2. 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。
7.1.2 集合框架
Java的集合框架 从整体上可以分为两种:
- Collection接口:该接口下的所有子孙均存储的是单一对象
- Map接口 :该接口下的所有子孙均存储的是key-value(键值对)形式的数据
另外还有三个分支,均是为上述服务的。
Iterator(迭代器):主要用于遍历Colleciton接口的及其子类而设计。
Comparator(比较器): 在集合中存储对象时候,用于对象之间的比较
Collecitons 是工具类:注意该类名带个s,一般就表示工具类。里面提供了N多静态方法,来对Colleciton集合进行操作。
7.1.3 Collect集合常用功能
- public boolean remove(E e) 把给定的对象在当前集合中删除
- public boolean contains(E e) 判断当前集合中是否包含指定对象
- public boolean isEmpty() 判断当前集合是否为空
- public int size() 返回集合中元素个数
- public Object[] toArray() 把集合中的元素存储到数组中
- public void clear() 清空集合中的元素
/**
* @author wangquan 1126909120@qq.com
* @create 2019/3/10 9:58
* @since JDK8u191
*/
public class CollectionTest {
public static void main(String[] args) {
Collection<String> s = new ArrayList<>();
System.out.println(s);
//public boolean add(E e)给指定的对象添加到元素中
boolean b1 = s.add("张三");
boolean b2 = s.add("李四");
boolean b3 = s.add("王五");
boolean b4 = s.add("赵六");
boolean b5 = s.add("田七");
System.out.println(s);
//public boolean remove(E e)把给定的对象在当前集合中删除
boolean b6 = s.remove("田七");
System.out.println(s);
//public boolean contains(E e)判断当前集合中是否包含指定对象
boolean b7 = s.contains("田六");
System.out.println(b7);
//public boolean isEmpty()判断当前集合是否为空
boolean b8 = s.isEmpty();
System.out.println(b8);
//public int size()返回集合中元素个数
int b9 = s.size();
System.out.println(b9);
//public Object[] toArray()把集合中的元素存储到数组中
Object[] b10 = s.toArray();
for (int i = 0; i < b10.length; i++) {
System.out.println(b10[i]);
}
//public void clear()清空集合中的元素
s.clear();
System.out.println(s);
}
}
7.1.4 Iterator接口
如果需要遍历集合中的所有元素。需要使用到接口java.util.Iterator。Iterator接口也是Java集合中的一员,但它与Collection、Map接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,所以Iterator对象也被称为迭代器。
Iterator接口的常用方法如下:
- public E next():返回迭代的下一个元素。
- public boolean hasNext():如果仍有元素可以迭代,则返回 true。
/**
* @author wangquan 1126909120@qq.com
* @create 2019/3/10 10:20
* @since JDK8u191
*/
public class IteratorTest {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("迪丽热巴");
coll.add("古力娜扎");
coll.add("马儿扎哈");
coll.add("赵丽颖");
coll.add("刘亦菲");
//使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
Iterator<String> it = coll.iterator();
// boolean b = it.hasNext();
// String s = it.next();
// System.out.println(s);
//使用while循环遍历更方便
while (it.hasNext()){
System.out.println(it.next());
}
}
}
7.1.5 增强for循环
- 增强for循环(也称for each循环)是JDK1.5以后出来的高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
格式:
for(元素的数据类型 变量 : Collection集合或数组){
//操作代码
}
代码实现:
**
* 增强for循环
* @author wangquan 1126909120@qq.com
* @create 2019/3/10 10:53
* @since JDK8u191
*/
public class ForeachTest {
public static void main(String[] args) {
demo01();
demo02();
}
private static void demo01() {
int[] array = {1, 2, 3, 4, 5};
for (int a : array) {
System.out.println(a);
}
}
private static void demo02() {
ArrayList<String> arr = new ArrayList<>();
arr.add("aaa");
arr.add("bbb");
arr.add("ccc");
for (String i :
arr) {
System.out.println(i);
}
}
}
7.2 泛型
7.2.1 泛型的概念
泛型:是一种未知的数据类型,当我们不知道用什么数据类型的时候,可以使用泛型。泛型也可以看成是一个变量,可以接收所有的数据类型
E e : Element元素
T t : Type类型
ArrayList集合在定义的时候,不知道集合中都会存储什么类型的数据,所以使用泛型
public class Generic<E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
7.2.2 使用泛型的好处
- 运行时期的ClassCastException,转移到了编译时期变成了编译失败。
- 避免了类型强转的麻烦。
例如:
public class GenericDemo2 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<String>();
list.add("abc");
list.add("itcast");
// list.add(5);//当集合明确类型后,存放类型不一致就会编译报错
// 集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型
//使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
//当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型
System.out.println(str.length());
}
}
}
7.2.3 定义和使用含有泛型的类
格式:
修饰符 class 类名<代表泛型的变量> {
}
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 19:07
* @since JDK8u191
*/
public class MyGenericClass<MVP> {
//没有MVP类型,在这里代表 未知的一种数据类型 未来传递什么就是什么类型
private MVP mvp;
public void setMVP(MVP mvp) {
this.mvp = mvp;
}
public MVP getMVP() {
return mvp;
}
}
class GenericClassDemo {
public static void main(String[] args) {
// 创建一个泛型为String的类
MyGenericClass<String> my = new MyGenericClass<String>();
// 调用setMVP
my.setMVP("大胡子登登");
// 调用getMVP
String mvp = my.getMVP();
System.out.println(mvp);
//创建一个泛型为Integer的类
MyGenericClass<Integer> my2 = new MyGenericClass<Integer>();
my2.setMVP(123);
Integer mvp2 = my2.getMVP();
}
}
7.2.4 定义和使用含有泛型的方法
定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){
}
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 19:07
* @since JDK8u191
*/
public class MyGenericMethod {
public <MVP> void show(MVP mvp) {
System.out.println(mvp.getClass());
}
public <MVP> MVP show2(MVP mvp) {
return mvp;
}
}
//调用方法时,确定泛型的类型
class GenericMethodDemo {
public static void main(String[] args) {
// 创建对象
MyGenericMethod mm = new MyGenericMethod();
// 演示看方法提示
mm.show("aaa");
mm.show(123);
mm.show(12.45);
}
}
7.2.5 定义和使用含有泛型的接口
定义格式:
修饰符 interface接口名<代表泛型的变量> {
}
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 19:07
* @since JDK8u191
*/
public interface MyGenericInterface<E>{
public abstract void add(E e);
public abstract E getE();
}
//使用格式
class MyImp1 implements MyGenericInterface<String> {
@Override
public void add(String e) {
// 省略...
}
@Override
public String getE() {
return null;
}
}
7.2.6 泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
- 通配符基本使用
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
泛型只能作为方法的参数使用,不能创建对象使用。
/**
* 泛型通配符
*
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 19:07
* @since JDK8u191
*/
public class GenericTest {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
getElement(list1);
Collection<String> list2 = new ArrayList<String>();
getElement(list2);
}
//?代表可以接收任意类型
public static void getElement(Collection<?> coll) {
}
}
通配符的高级使用—受限泛型
设置泛型的时候,实际上只要是类就可以任意设置的,但在Java的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式: 类型名称 <? extends 类 > 对象名称
- 意义: 只能接收该类型及其子类
泛型的下限:
- 格式: 类型名称 <? super 类 > 对象名称
- 意义: 只能接收该类型及其父类型
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement(list1);
getElement(list2);//报错
getElement(list3);
getElement(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll) {
}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll) {
}
7.3 数据结构
7.3.1 数据结构 栈
栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单的说:采用该结构的集合,对元素的存取有如下的特点:
- 栈的存储顺序是先进后出,栈的入口和出口都在集合的同一侧
- 先进后出:存进去的元素,要在后它后面的元素依次取出后,才能取出该元素
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置
7.3.2 数据结构 队列
- 队列的存储顺序是先进先出
- 队列的入口和出口在集合的两侧,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素
7.3.3 数据结构 数组
数组特点:
- 查询快:通过索引,可以快速访问指定位置的元素
- 增删慢:
- 指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置
- 指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中
7.3.4 数据结构 链表
链表特点:
- 查询慢:链表中的地址不是连续的,每次查询元素,都必须从头开始查询
- 增删块:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删块
链表中的每一个元素也称之为一个节点,一个节点包含了一个数据源(存储数组),两个指针域(存储地址)
单向链表:链表中只有一条链子,不能保证元素的顺序(存储元素和取出元素的顺序可能不一致)
双向链表:链表中有两条链子,有一条链子是专门记录元素的顺序,是一个有序的集合
7.3.5 数据结构 红黑树
7.4 List集合
7.4.1 List集合
List接口特点:
- 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
- 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
- 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
List接口中带索引的方法(特有)
- public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
- public E get(int index) :返回集合中指定位置的元素。
- public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
- public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素
package cn.itcast.day12;
import java.util.ArrayList;
import java.util.List;
/**
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 20:07
* @since JDK8u191
*/
public class ListTest {
public static void main(String[] args) {
//创建一个List集合对象,多态
List<String> list = new ArrayList<>();
//使用add方法往集合中添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("a");
//打印集合
System.out.println(list);//[a, b, c, d, a]
//public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
//在c和d中添加一个wangquan
list.add(3, "wangquan");
System.out.println(list);
// String remove(int index) 删除指定位置元素 返回被删除元素
// 删除索引位置为2的元素
System.out.println("删除索引位置为2的元素");
System.out.println(list.remove(2));
System.out.println(list);
// String set(int index,String s)
// 在指定位置 进行 元素替代(改)
// 修改指定位置元素
list.set(0, "王权");
System.out.println(list);
// String get(int index) 获取指定位置元素
// 跟size() 方法一起用 来 遍历的
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//还可以使用增强for
for (String string : list) {
System.out.println(string);
}
}
}
7.4.2 List的子类
ArrayList集合
java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为
查询数据、遍历数据,所以 ArrayList 是最常用的集合
LinkedList 集合
java.util.LinkedList 集合数据存储的结构是链表结构。方便元素添加、删除的集合
LinkedList是一个双向链表
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方
法我们作为了解即可:
- public void addFirst(E e) :将指定元素插入此列表的开头。
- public void addLast(E e) :将指定元素添加到此列表的结尾。
- public E getFirst() :返回此列表的第一个元素。
- public E getLast() :返回此列表的最后一个元素。
- public E removeFirst() :移除并返回此列表的第一个元素。
- public E removeLast() :移除并返回此列表的最后一个元素。
- public E pop() :从此列表所表示的堆栈处弹出一个元素。
- public void push(E e) :将元素推入此列表所表示的堆栈。
- public boolean isEmpty() :如果列表不包含元素,则返回true。
import java.util.LinkedList;
/**
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 20:26
* @since JDK8u191
*/
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
//添加元素
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
System.out.println(link);
// 获取元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
// 删除元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
//判断集合是否为空
while (!link.isEmpty()) {
//弹出集合中的栈顶元素
System.out.println(link.pop());
}
System.out.println(link);
}
}
7.5 Set集合
java.util.Set接口 extends Collection接口
Set接口的特点:
- 不允许存储重复的元素
- 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
7.5.1 Hash集合
HashSet集合 implements Set接口
HashSet特点:
- 不允许存储重复的元素
- 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
- 是一个无序集合,存储元素和取出元素的顺序可能不一致
- 底层是一个哈希表结构(查询速度非常快)
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 20:58
* @since JDK8u191
*/
public class HashSetTest {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
//使用add方法往集合中添加元素
set.add(1);
set.add(3);
set.add(2);
set.add(4);
set.add(2);
//使用迭代器遍历Set集合
Iterator<Integer> it = set.iterator();
while (it.hasNext()){
Integer n = it.next();
System.out.println(n);//1 2 3 4
}
}
}
7.5.2 哈希表
- 哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的额地址,不是数据实际存储的物理地址)
- 在Object类有一个方法 可以获得对象的哈希值:
int hashCode() 返回对象的哈希值
使用hashCode()方法获取哈希值:
/**
* @author wangquan 1126909120@qq.com
* @create 2019/3/12 21:15
* @since JDK8u191
*/
public class HashCodeTest {
public static void main(String[] args) {
Person p1 = new Person();
//获取对象p的哈希值
int h1 = p1.hashCode();
Person p2= new Person();
int h2 = p2.hashCode();
//打印输出
System.out.println(h1);//460141958
System.out.println(h2);//1163157884
System.out.println(p1);//cn.itcast.day12.Person@1b6d3586
System.out.println(p2);//cn.itcast.day12.Person@4554617c
System.out.println(p1==p2);//false
//String类的哈希值
//String类重写Object类的hashCode方法
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());//96354
System.out.println(s2.hashCode());//96354
//重地和通话的hashCode值是一样的
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
}
}
class Person{
}