集合概述
集合:集合是java中提供的一种容器,可以用来存储多个数据。
- 集合和数组既然都是容器,它们有啥区别呢?
- 数组的长度是固定的。集合的长度是可变的。
数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储
集合概述
集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection
和双列集合java.util.Map
Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List
和java.util.Set
。其中,List
的特点是元素有序、元素可重复。Set
的特点是元素无序,而且不可重复。List
接口的主要实现类有java.util.ArrayList
和java.util.LinkedList
,Set
接口的主要实现类有java.util.HashSet
和java.util.TreeSet
。
整个集合类的继承体系。
橙色框里填写的都是接口类型
蓝色框里填写的都是具体的实现类
集合本身是一个工具,它存放在java.util包中。在Collection
接口定义着单列集合框架中最最共性的内容。
Collection相关Api
单列集合(List和Set)通用的一些方法
方法如下:
int size();
获取集合的长度boolean isEmpty();
判断集合是否为空void clear();
清空集合boolean contains(Object element);
判断当前元素是否存在于集合内boolean add(Object element);
向集合中添加元素boolean remove(Object element);
根据参数删除集合中的元素boolean containsAll(Collelction c);
判断一个集合中的元素是否存在于当前的集合中boolean addAll(Collelction c);
向一个集合内部添加另一个集合boolean removeAll(Collelction c);
在一个集合中删除参数指定的集合boolean retionAll(Collection c);
获取两个集合的交集Object[] toArray
把一个集合转换为Object数组<T>T[] toArray(T[] a);
指定操作的泛型类型,并把内容返回Iterator<E>Iterator();
为iterator接口实例化booleam equals(Object o);
int hashCode();
本接口中一共定义了 15个方法
开发中不会直接使用 Collection 接口。而使用其操作的子接口:List、Set
Collection增加Api
boolean add(Object element);
向集合中添加元素
boolean addAll(Collelction c);
向一个集合内部添加另一个集合
Collection删除Api
boolean add(Object element); 向集合中添加元素
boolean addAll(Collelction c); 向一个集合内部添加
Collection修改Api
public void clear()
:清空集合中所有的元素。
list.clear();
Collection判断Api
boolean isEmpty();
判断集合是否为空
boolean contains(Object element);
判断当前元素是否存在于集合内
boolean containsAll(Collelction c);
判断一个集合中的元素是否存在于当前的集合中
boolean retionAll(Collection c);
获取两个集合的交集
Collection获取功能
int size();
获取集合的长度
Object[] toArray
把一个集合转换为Object数组
// 获取 集合容器的大小
int size = list.size();
把一个集合转换为Object数组
Collection list = new ArrayList();
Object[] oo = list.toArray();
项目2 泛型入门
泛型:可以简单理解为限制了集合中存入的数据的类型。
泛型可以定义在 类 方法 接口前面
泛型中的类型包括:
Object String Number Integer
泛型各个定义的语法
在类中
修饰符 class类名<代表泛型的变量> {
}
泛型类实例:
//MVP在这里代表 未知的一种数据类型 未来传递什么就是什么类型
public class One <MVP>{
//有参数无返回值的方法
public void show(MVP mvp){
System.out.println(mvp);
}
public static<MVP> getShow(MVP mvp){
return mvp;
}
}
在类中什么时候被确定的?
在创建对象的时候确定
在方法中
定义格式
修饰符 返回类型 方法名参数(){
}
实例:
public <T> void show(ACE ace){
System.out.println(ace.getClass());
}
方法中什么时候被确定的?
调用方法时,确定泛型的类型
在接口中
格式:
修饰符 interface 接口名{ }
类型什么时候确定?
有两种方式 比如下方实例
实例:
//一 定义接口
public interface C1<Q> {
public abstract void show();
public abstract Q getQ();
}
//2-1 定义实现类——确定泛型类型
public class MyImp implements C1<String>{
@Override
public void show(String s){
//省略
}
public String getQ(){
//省略
}
}
//此时泛型Q的类型就是String
//—————————————————————————
//2-2 定义实现类——不确定泛型类型(子类必须也是泛型类)
public class MyImp2<Q> implement C1<Q>{
@Override
public void show(Q q){
//省略
}
public Q getQ(){
//省略
}
}
//第三 确定泛型,需要新的实现类
public class Demo{
public static void main(String[] args) {
//创建对象,定义实现类型
MyImp2<String> my = new MyImp2<String>();
my.add("aa");
}
}
泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。
不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符
通配符基本使用
public static void getElement(Collection<?> coll){}
//?代表可以接收任意类型
通配符高级使用----受限泛型
JAVA的泛型中还可以指定一个泛型的上限和下限。取决于extends和super关键字
泛型的上限:
- 格式: 类型名称 <? extends 类 > 对象名称
- 意义: 只能接收该类型及其子类
泛型的下限:
- 格式: 类型名称 <? super 类 > 对象名称
- 意义 : 只能接收该类型及其父类型
-
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
定义集合时,同时定义集合中元素的类型。
好处
- 增强程序的可读性和稳定性。
- 避免了强制类型转换的麻烦。
- 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
public class Demo06{
public static void main(String[] args) {
/*
Set s = new HashSet();
s.add("aaa");
s.add("bbb");
s.add(111);
s.add(222);
s.add(new Date());
s.add(new User(1,"aaa",20));
System.out.println(s.size());
for(Object obj:s){
System.out.println(obj);
}
*/
Set<String> s = new HashSet<>();
s.add("aaa");
s.add("bbb");
s.add("ccc");
//s.add(111);
Set<Integer> s2 = new HashSet<>();
s2.add(1);
s2.add(2);
//s2.add("aaa");
}
}
项目3 Iterator迭代器
Iterator接口
java.util.Iterator
。Iterator
接口也是Java集合中的一员,但它与Collection
、Map
接口有所不同,Collection
接口与Map
接口主要用于存储元素,而Iterator
主要用于迭代访问(即遍历)Collection
中的元素,因此Iterator
对象也被称为迭代器。
迭代:即Collection集合元素的通用获取方式。
在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
Iterator接口的常用方法如下:
public E next()
:返回迭代的下一个元素。 public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。
案例展示:
public class IteratorDemo {
public static void main(String[] args) {
// 使用多态方式 创建对象
Collection<String> coll = new ArrayList<String>();
// 添加元素到集合
coll.add("串串星人");
coll.add("吐槽星人");
coll.add("汪星人");
//遍历
//使用迭代器 遍历 每个集合对象都有自己的迭代器
Iterator<String> it = coll.iterator();
// 泛型指的是 迭代出 元素的数据类型
while(it.hasNext()){ //判断是否有迭代元素
String s = it.next();//获取迭代出的元素
System.out.println(s);
}
}
}
迭代器的实现原理
在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历
项目4 增强for
增强for循环(也称for each循环),它的内部原理也是个Iterator迭代器。目标只能是Collection或者是数组。
作用:
专门用来遍历数组和集合的
缺点:
遍历的过程中,不能对集合中的元素进行增删操作。
格式:
for(元素的数据类型 变量 : Collection集合or数组){
//写操作代码
}
项目5 set
set和list一样同样继承Collection
接口
它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格
set集合特点:
无序和唯一
项目6set实现类HashSet
HashSet原理:用Hash技术实现的Set结构。
如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的
因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。
总结:
元素的哈希值是通过元素的hashcode方法 来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。
哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。
项目7 LinkedHashSet
是链表和哈希表组合的一个数据存储结构。
Set set = new LinkedHashSet();
项目8 TreeSet
TreeSet是一个可排序集合,基于红黑树(自平衡二叉树),默认按元素的自然顺序升序排列。
TreeSet t = new TreeSet<>();
当存入的是基本数据类型和字符串时,TreeSet有自动升序排列的能力。而当存入的是自定义引用类型,遍历时会抛出异常
项目9TreeSet集合排序的两种方式
第一种Comparable接口
第一种方式让元素自身具备比较性,也就是元素需要实现Comparable接口,重写compareTo 方法
重写:
第一 子类实现 Comparable接口的方法 implements
implements Comparable<属性范围>
第二 重写方法
idea中 generate——compareTo;
第三 方法排序注意
compareTo 方法:this和参数的比较。
- 返回正数 this比参数大 (this排在参数后面)
- 返回负数 this比参数小 (this排在参数前面)
- 返回零 this和参数相等(认为是重复数据)
return this.price-o.getPrice() ; 升序
return o.getPrice()-this.price ;降序
第二种Comparator接口
当元素自身不具备比较性,或者元素自身具备的比较性不是所需的。那么这时只能让容器自身具备。
定义一个类实现Comparator 接口,重写compare方法。并将该接口的子类对象作为参数传递给TreeSet集合的构造方法。
重写:
public int compare(Teacher t1, Teacher t2) {
// t1>t2 返回整数
// t1<t2 返回负数
// t1==t2 返回0
//return t1.getId()-t2.getId();
return t1.getName().compareTo(t2.getName());
}
注意:当Comparable比较方式,及Comparator比较方式同时存在,以Comparator比较方式为主。
TreeSet案例
需求:
将字符串中的数值进行排序。
例如String str=“8 10 15 5 2 7”;
使用 TreeSet完成。
思路:
1、将字符串切割。
2、可以将这些对象存入TreeSet集合。
因为TreeSet自身具备排序功能。
实例:
public class Demo1 {
public static void main(String[] args) {
String str = "8 10 15 21 16";
String[] sp = str.split("\\s+");
TreeSet<Integer> t = new TreeSet<>();
for(String s:sp){
t.add(Integer.parseInt(s));
}
System.out.println(t);
}
}
项目10 list接口
list特点:
List接口是Collection的子接口,实现List接口的容器类中的元素是有顺序的,而且可以重复。
有序
可重复
内部结构为 数组结构和链表结构
主要实现类有 ArrayList
和 LinkedList
。
list 独有的方法取 改 填 删
Object get(int index);
通过索引获取元素
Object set(int index,Object element);
修改某出索引元素
void add(int index,Object element);
在某处索引添加元素
Object remove(int index);
根据索引删除元素
项目11 ArrayList
java.util.ArrayList合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以
ArrayList`是最常用的集合
项目12 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 boolean isEmpty()
:如果列表不包含元素,则返回true。
项目12——*** ArrayList与LinkedList的区别?
面试常考
- 他们都是线性表,但是ArrayList基于数组(顺序存储),LinkedList基于链表(链式存储)
- 由于实现的不同,ArrayList在随机访问元素时性能较高,插入和删除元素时效率较低,LinkedList则反之。
项目13 Vector和Stack(栈)
Vector: 描述的是一个线程安全的ArrayList。
- Vector与ArrayList的区别
ArrayList: 单线程效率高。
Vector : 多线程安全的,所以效率低。
常用方法 取尾 指定索引 取全部封装 Enumeration
void addElement(E obj)
在集合末尾添加元素
E elementAt( int index)
返回指定角标的元素
Enumeration elements()
返回集合中的所有元素,封装到Enumeration对象中
Enumeration接口: 枚举
boolean hasMoreElements()
测试此枚举是否包含更多的元素。
E nextElement()
如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。
public static void main(String[] args) {
Vector v = new Vector();
v.addElement("aaa");
v.addElement("bbb");
v.addElement("ccc");
System.out.println( v );
System.out.println( v.elementAt(2) ); // ccc
// 遍历Vector遍历
Enumeration ens = v.elements();
while ( ens.hasMoreElements() ){
System.out.println( ens.nextElement() );
}
}
Stack(栈)
常用方法:
boolean | empty() 测试堆栈是否为空。 |
---|---|
E | peek() 查看堆栈顶部的对象,但不从堆栈中移除它。 |
E | pop() 移除堆栈顶部的对象,并返回该对象作为此函数的值。 |
E | push(E item) 把项压入堆栈顶部。 |
int | search(Object o) 返回对象在堆栈中的位置,以 1 为基数。 |
Stack类是Vector类的子类。特点是先进后出。
项目14可变参数()
我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式
修饰符 返回值类型 方法名(参数类型… 形参名){ }
修饰符 返回值类型 方法名(参数类型[] 形参名){ }
/*
* 完成数组 所有元素的求和 原始写法
public static int getSum(int[] arr){
int sum = 0;
for(int a : arr){
sum += a;
}
return sum;
}
*/
//可变参数写法
public static int getSum(int... arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
输出完全相等
项目15 Collections
java.utils.Collections
是集合工具类,用来对集合进行操作。
方法有:
sort(List<T> list);
对List即可进行默认排序
addAll(Collection<T>,T...e);
向集合中添加可变参数元素
fill(List<E> list, E e):
将list集合中的所有元素都填充为元素e
int frequency(Collection<E> c, E e):
返回在集合c中的元素e的个数
max、min:
获取集合的最大值或者最小值
replaceAll(List<E> list, E oldVal, E newVal):
将集合list中的所有指定老元素oldVal都替换成新元素newVal
reverse(List<E> list):
将参数集合list进行反转
shuffle(List<E> list):
将list集合中的元素进行随机置换
swap(List<E> list, int a, int b):
将a索引和b索引的元素进行交换
synchronizedXxx
方法系列:将一个线程不安全的集合传入方法,返回一个线程安全的集合
contains API源码
判断一个对象中是否拥有某个属性
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
```