集合
集合就是java中存放数据的一个容器
数组:
- 长度固定,不能再去添加元素
集合类
- 长度可变
- 能存储任意对象
- 长度是随着元素的增加而增加的
数组和集合的区别
- 数组能存基本数据类型和引用类型
- 集合当中只能存放引用数据类型,直接放基本数据,也会自动装箱(把基本数据类型转换成对象),集合当中只能存放对象
- 数组的长度是固定的,不能再去增长,集合长度是可变的,根据元素的增长而增加
什么时候使用数组,什么时候使用集合类
- 如果元素个数是固定的,推荐使用数组,否则推荐使用集合类
Collection
Collection接口是 (java.util.Collection)是Java集合类的顶级接口之一,整个集合框架就围绕一组标准接口而设计.
Collection下的集合常用的分为俩种,有序的List集合(接口),无序的set集合(接口)
list下有三种实现类Arraylist,LinkedList,Vector
set下有俩种 实现类hashSet和TreeSet
boolean add(E e)确保此collection包含指定的元素
boolean addAll(Collection<? extends E> c)将指定集合中的所有元素添加到此集合
void clear()从此集合中删除所有元素
boolean contains(Object o)如果此集合包含指定的元素返回true)
boolean containsAll(Object o)如果此集合包含指定的元素,则返回true
boolean rquals(Object o)将指定的对象与此集合进行比较已获得相等性
int hashCode()返回此集合的哈希码值
beeloean isEmpty()如果此集合不包含元素,则返回true
default Stream<E> parallelStream()返回可能的
boolean remove(Object o)从该集合中删除指定元素的单个实例
boolean removeAll(Collection<?>c)删除集合中包含有的所有此集合的元素
default boolean removeIf(Predicate<? super E>filter)删除满足给定谓词的此集合的所有元素
boolean retainAll(Collection<?> c)保留此集合中包含在指定集合中的元素
int size()返回集合中的元素数
default Spliterator<E> spliterator()创建一个Spliterator在这个集合中的元素
default Stream<E> stream()返回此集合作为源顺序Stream
Object[]toArray(T[]a)返回包含此集合中所有元素的数组
<T> T[] toarray(T[] a)返回包含此集合中所有元素的数组,返回数组的运行类型是制定数组的运行时类型
List
Collenction
下文用ArrayList来讲解Collenction的内容
list可以添加重复元素,set不能添加,list是一个有序的集合
arrayList添加元素
package com.company;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class Main {
public static void main(String[] args) {
//多台写法运行时候表现出来是子类的特征
//集合当中存的都是对象
Collection c=new ArrayList();
//开发当中list不会接受add方法的返回参数,是给set方法使用的
// 因为set方法不能添加重复元素,如果添加了重复元素就会返回false
//list会一直返回true
boolean abc = c.add("abc");
c.add(10);//自动装箱(把基本数据类型转换成对象)
c.add(true);//编译成字节码的时候,会编译成 c.add(Boolean.valueOf(10))
System.out.println(c);//ArrayList当中的爷爷类AbstractCollection中覆盖了tostring方法
}
}
List删除元素
package com.company;
import java.util.ArrayList;
import java.util.Collection;
public class Main {
public static void main(String[] args) {
Collection c2=new ArrayList();
c2.add("a");
c2.add("b");
c2.add("a");
c2.add("a");
c2.add("c");
//可以从集合移除指定的元素
c2.remove("b");//成功返回true,失败返回false
System.out.println(c2);//[a, a, a, c]
//清空集合当中所有元素
c2.clear();
System.out.println(c2);//[]
//判断集合是否为空
System.out.println(c2.isEmpty());//true
}
}
集合的遍历
package com.company;
import java.util.ArrayList;
import java.util.Collection;
public class Main {
public static void main(String[] args) {
Collection c1=new ArrayList();
c1.add("a");
c1.add("b");
c1.add("a");
c1.add("a");
Object[] objects = c1.toArray();
for (int i=0;i<objects.length;i++){
System.out.println(objects[i]);
}
}
}
遇到对象的时候,需要将集合toArray()方法会自动将对象升级为Object类型,用到对象中的方法时,需要强制类型转换降级才可以
集合的常用方法
package com.company;
import java.util.ArrayList;
import java.util.Collection;
public class Main {
public static void main(String[] args) {
Collection c1=new ArrayList();
c1.add("a");
c1.add("b");
c1.add("a");
c1.add("a");
c1.add("e");
Collection c2=new ArrayList();
c2.add("a");
c2.add("b");
c2.add("c");
c2.add("d");
//把c2中的所有元素添加到c1当中
// c1.addAll(c2);
// System.out.println(c1);//[a, b, a, a, e, a, b, c, d]
//去除c1中c2有的元素
// c1.removeAll(c2);
// System.out.println(c1);//[e] c1与c2的差集
// boolean b = c1.containsAll(c2);//全判断c1是不是包含了c2的全部元素
// System.out.println(b);//
c1.retainAll(c2);//取交集,把交集结果赋值给调用者,如果改变了返回true,没有改变返回false
System.out.println(c1);
}
}
迭代器
在每一个实现类中都定义了自己的迭代方法
package com.company;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
Collection c=new ArrayList();
c.add("a");
c.add("b");
c.add("a");
c.add("a");
//迭代器遍历
//这里是实现Iterator接口的类,在ArrayList当中定义
Iterator it=c.iterator();//放到Iterator内容中会自动类型提升
//获取得迭代器当中的内容
// Object obj = it.next();//把当前游标的内容取出来,当前游标向后走一位
// System.out.println(obj);//a
// obj = it.next();//把当前游标的内容取出来,当前游标向后走一位
// System.out.println(obj);//b
// boolean res = it.hasNext();//有下一个元素返回true,没有返回false
// System.out.println(res);//true
while(it.hasNext()){//底层有一个角标,如何角标和长度相等怎返回false
//使用对象中的方法的时候同样需要向下转型
System.out.println(it.next());//abaa
}
}
}
List独有的方法
package com.company;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
//list有角标
List list=new ArrayList();
list.add("a");
list.add("b");
list.add("c");
//根据角标来添加元素
//角标一定要<=size,大于size就会报错,带角标的添加只能在`list当中才能使用
list.add(1,"myxq");
System.out.println(list);//[a, myxq, b, c]
//获取指定角标的元素
System.out.println(list.get(0));//a
for(int i=0;i<list.size();i++){
//根据角标遍历元素
System.out.println(list.get(i));//a myxq b c
}
}
}
并发修改异常
在获取迭代器时,回合集合进行关联,保持李阿扁数据一致,内部会有一个modCount和ModCount,默认是相等的,modCount:集合记录修改次数,expectedModCount迭代器当中记录集合修改的次数,在我们取元素的时候,都会先做一个判断,如果不相等就会抛出一个并发修改异常
在迭代集合的时候不允许修改集合结构,会报错(增加和删除都不行)
java.util.ConcurrentModificationException
每一次调用next方法会调用比较modCount和expectedModCount,如果不相等则抛出错误
modCount:集合修改的次数
expectedModCount:迭代器当中集合修改的次数
迭代器当中的remove会自动把俩个修改的次数给统一
迭代器listIterator
在list中有自己的迭代器
Modifier and Type | Method and Description |
---|---|
void | add(E e) 将指定的元素插入列表(可选操作)。 |
boolean | hasNext() 返回 true 如果遍历正向列表,列表迭代器有多个元素。 |
boolean | hasPrevious() 返回 true 如果遍历反向列表,列表迭代器有多个元素。 |
E | next() 返回列表中的下一个元素,并且前进光标位置。 |
int | nextIndex() 返回随后调用 next() 返回的元素的索引。 |
E | previous() 返回列表中的上一个元素,并向后移动光标位置。 |
int | previousIndex() 返回由后续调用 previous() 返回的元素的索引。 |
void | remove() 从列表中删除由 next() 或 previous() 返回的最后一个元素(可选操作)。 |
void | set(E e) 用 指定的元素替换由 next() 或 previous() 返回的最后一个元素(可选操作)。 |
利用list的迭代器listIterator在指定元素后面追加固定的元素
倒着打印元素
package com.company;
import java.util.ArrayList;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add("1");
list.add("2");
list.add("3");
//list当中特有 的迭代器
ListIterator it = list.listIterator();
while(it.hasNext()){
String str=(String)it.next();
if(str.equals("2")){
//这里也做过同步
//set方法不需要同步,因为没有修改元素
it.add("23333");//[1, 2, 23333, 3]
}
}
System.out.println(list);
//因为使用的是同一个迭代器,所以现在角标在末尾
while (it.hasPrevious()){
System.out.println("游标为"+it.previousIndex());//游标逐渐减小
//倒着打印整个集合,如果注释之后就会死循环
//这是由于角标没有向前移动的关系
System.out.println(it.previous());
}
}
}
ArrayList底层实现是数组
数组初始化就不会改变,当数组的长度不够的时候,把原来的数组赋值出来,再创建一个新数组容量增加50%,把复制的数组放到新数组当中,原来的数组扔掉
添加元素
把对应的值通过一个值覆盖后一个值的方式,依次往后移动,把要插入的值覆盖到指定的位置(相当于把其他值全部后移)
删除元素
把要删除的后位置的一个值,赋值到要删除的位置,后续依次进行复制覆盖,把最后一个值变为null
排序
排序可以直接通过equals重写,来让自定义返回值,contanins比较其实调用的是equals方法,如果没有覆盖不这个方法会直接去调用Object中的方法,Object中的equals默认比较的是地址
package com.company;
import java.util.ArrayList;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add("1");
list.add("2");
list.add("2");
list.add("3");
ArrayList singleEle = getSingleEle(list);
System.out.println(singleEle);
}
//去重的函数
public static ArrayList getSingleEle(ArrayList list){
ArrayList newlist=new ArrayList();
ListIterator it = list.listIterator();
while(it.hasNext()){
Object obj = it.next();
if (!newlist.contains(obj)){
newlist.add(obj);
}
}
return newlist;
}
}
LinkedList
底层是一个链表的结构
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
优点:链表的插入和删除比较快,只需要切换前后的指向地址
缺点:查询和修改比较的快
LinkedList特有方法
package com.company;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
LinkedList list = new LinkedList<>();
list.add("c");
list.add("c");
ListIterator it=list.listIterator();
//特有
//往第一个位置添加元素
list.addFirst("one");
//在最后一个位置添元素
list.addLast("last");
//移除第一个元素
list.removeFirst();
//移除集合中最后一个元素
list.removeLast();
//也可以根据角标获取元素
//内部实现是通过一个个遍历节点来实现的,所以linked查询是比较慢的
System.out.println(list.get(0));//c
}
}
Vector
jdk1.2之后为实现list接口,1.2之前才不是list集合里的
内部实现也是数组
vector没有ArrayList使用的平凡
vector会加锁,使用起来更加的安全
package com.company;
import java.util.*;
public class Main {
public static void main(String[] args) {
Vector vc=new Vector();
vc.add("a");
vc.add("b");
//除了使用ListIterator之外还能使用自己本身的迭代器
Enumeration e=vc.elements();
while (e.hasMoreElements()){
System.out.println(e.nextElement());
}
}
}
集合与数组互相转换
封装过之后,可以用面向对象的方式去操作数组(除了不能添加和删除元素之外,集合当中的其他东西都可以使用)
package com.company;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
int []arr={10,20,30};
//把数组转换成集合
//基本数据类型
List list= Arrays.asList(arr);
// list.add(10);//数组转集合不能添加元素
Object o = list.get(0);//只能get第0位,也就是数组本身
System.out.println(o);//输出的是地址,转成集合时,是把基本数据类型的数组,当做是一个对象
System.out.println(list.size());//1
System.out.println("--------------------------------------------");
//引用数据类型的数组采取转成集合
//引用数据类型
Integer[]arr2={10,20,30};
List<Integer> list2 = Arrays.asList(arr2);
//可以使用get,不能添加和删除
list2.get(1);
System.out.println(list2);//[10, 20, 30]
//集合转数组
List<String>list3=new ArrayList<>();
list3.add("a");
list3.add("b");
//集合数组
Object[] objects=list3.toArray();
//指定开辟空间大小,如果空间小于size会自动创建和size一样的空间大小
//如果大于size,多余的空间为null
Object[] strArr = list3.toArray(new String[0]);
System.out.println(Arrays.toString(strArr));
}
}
底层的实现
集合嵌套集合
package com.company;
import java.util.ArrayList;
import java.util.List;
class Person{
String name;
Person(String name){
this.name=name;
}
}
public class Main {
public static void main(String[]args){
Person per1=new Person("zs");
Person per2=new Person("ls");
ArrayList<Person> c1 = new ArrayList<>();
c1.add(per1);
c1.add(per2);
Person per3=new Person("zs");
Person per4=new Person("ls");
ArrayList<Person> c2 = new ArrayList<>();
c2.add(per3);
c2.add(per4);
//学科(集合当中存放集合)
List<List<Person>> x=new ArrayList<>();
x.add(c1);
x.add(c2);
//把所有班级当中的学生姓名打印出来
for (List<Person>g:x){
//取出每一个班级
for (Person per:g){
System.out.println(per.name);//循环嵌套输出嵌套的集合
}
}
}
}
Set
set当中的元素是无序的,Collection中的方法在Set中都适用
package com.company;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
public static void main(String[]args){
//set当中的元素是无序的
//set当中已经覆盖了toString方法
//set当中存在的元素都是无序的
HashSet<String> hs = new HashSet<>();
boolean res = hs.add("a");
boolean res2=hs.add("a");
hs.add("c");
hs.add("1");
System.out.println(res);//true
System.out.println(res2);//false
Iterator<String> iterator = hs.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//能够使用迭代器的都能使用foreach循环
System.out.println("-------");
for(String str:hs){
System.out.println(str);//a 1 c
}
}
}
自定义对象去重
package com.company;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Person{
String name;
int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", 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);
}
//在set集合当中要覆盖hashCode
//7之后的新特性,要比较的任意个数的变量,就跨可以返回根据字段对象生成的hash值
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Main {
public static void main(String[]args){
//想要set当中自定义对象去重
//覆盖equals方法
//覆盖hashcode方法
Set<Person> s=new HashSet<>();
s.add(new Person("张三",20));
s.add(new Person("张三",20));
s.add(new Person("李四",21));
s.add(new Person("李四",21));
System.out.println(s);//
}
}
HashSet
package com.company;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
class Cat{
String name;
Cat(String name){
this.name=name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Cat)) return false;
Cat cat = (Cat) o;
return Objects.equals(name, cat.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
public class Main {
public static void main(String[]args){
Cat c1=new Cat("mm");
Cat c2=new Cat("mm");
Set s=new HashSet();
//添加对象时,会调用hashCode方法
//hashCode不相同
s.add(c1);
s.add(c2);
System.out.println(s);
}
}
去重原理
每一个对象都会有一个hashcode,调用的时候会返回一个int值,hashcode,就是和内存地址对应的编号,每个对象的hash值都不一样,可以重写这个方法指定编号,在添加对象的时候,会调用对象的hashcode方法
比较的时候会先比较equals,如果c2兑现共和集合当中的hashCode不相同就会调用equals,当hashcode相同的时候回调用equals。
测试:生成1-20的随机数
package com.company;
import java.util.HashSet;
import java.util.Random;
public class Main {
public static void main(String[]args){
//在从1-20之间的随机数,获取10,不允许有重复
//使用 Random来生成随机数
Random r=new Random();
//创建存放生成结果的集合,HashSet
HashSet<Integer> hs = new HashSet<>();
//当结果大于HashSet.size大于10的时候,就不放,否则一直生成往里面放
while (hs.size()<10){
//生成1.20之间的随机数
int res=r.nextInt(20)+1;
//添加到集合中
hs.add(res);
}
System.out.println(hs);
}
}
LinkHashSet
LinkHashSet是hashset的子类,底层是使用链表实现的,是Set集合当中唯一的一个保证元素是怎么存怎么取的,相比于hashset性能要低一点
package com.company;
import java.util.LinkedHashSet;
public class Main {
public static void main(String[]args){
LinkedHashSet<String>set=new LinkedHashSet<>();
set.add("a");
set.add("1");
set.add("c");
set.add("e");
System.out.println(set);//[a, 1, c, e]
}
}
生成随机数
package com.company;
import java.util.HashSet;
import java.util.Random;
public class Main {
public static void main(String[]args){
//在从1-20之间的随机数,获取10,不允许有重复
//使用 Random来生成随机数
Random r=new Random();
//创建存放生成结果的集合,HashSet
HashSet<Integer> hs = new HashSet<>();
//当结果大于HashSet.size大于10的时候,就不放,否则一直生成往里面放
while (hs.size()<10){
//生成1.20之间的随机数
int res=r.nextInt(20)+1;
//添加到集合中
hs.add(res);
}
System.out.println(hs);
}
}
TreeSet
TreeSet它也不是按照添加的顺序展示的
TreeSet用来对元素进行排序,里面1也是可以保证元素的唯一
数字从小到打排序,字符串按照字母顺序依次比较,汉字是按照unicode码表来进行排序的
基本使用
package com.company;
import java.util.TreeSet;
public class Main {
public static void main(String[]args){
//TreeSet它也不是按照添加的顺序展示的
//TreeSet用来对元素进行排序,里面1也是可以保证元素的唯一
TreeSet<Integer> set=new TreeSet<Integer>();
set.add(10);
set.add(2);
set.add(2);//添加不进去,会返回false
set.add(9);
set.add(6);
System.out.println(set);
}
}
TreeSer当中存放的必须得是同一类型,自定义的对象不能直接添加到TreeSet当中,想要添加到Three当中必须要实现Comparable覆盖compareTo方法,String,Integer中都实现了这个接口
package com.company;
import java.util.TreeSet;
class Person implements Comparable<Person> {
String name;
int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
@Override
//如果返回0只添加第一个元素,
//如果是一个正数,都能添加到集合当中,顺序是怎么添加的怎么显示
//如果是一个负数,都能添加到集合当中,
public int compareTo(Person o) {
return 0;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[]args){
TreeSet<Person>set=new TreeSet<>();
set.add(new Person("张三",20));
set.add(new Person("李四",21));
System.out.println(set);//[Person{name='张三', age=20}]
}
}
TreeSet自定义排序实现
treeset底层是红黑树
package com.company;
import java.util.TreeSet;
class Person implements Comparable<Person> {
String name;
int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
@Override
//如果返回0只添加第一个元素,
//如果是一个正数,都能添加到集合当中,顺序是怎么添加的怎么显示
//如果是一个负数,都能添加到集合当中,
public int compareTo(Person o) {//这里传进来的元素是当前比较的元素
//如果比当前的小就会返回正数,并且添加到后方,大就会返回负数添加到前方
//输出的时候会从根节点开始查找是否有左边的下级元素,一直找到地依次向上输出
return this.age-o.age;
/*
//这样可以实现先对比年龄再对比姓名
int num=this.age-obj.age;
if(num==0){
//String中已经覆盖了compare方法
return this.name.compareTo(obj.name);
}else{
return num;
}
*/
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Main {
public static void main(String[]args){
TreeSet<Person>set=new TreeSet<>();
set.add(new Person("张三",20));
set.add(new Person("李四",21));
set.add(new Person("王五",22));
set.add(new Person("赵六",21));
//[Person{name='张三', age=20}, Person{name='李四', age=21}, Person{name='王五', age=22}]
System.out.println(set);
}
}
比较器
package com.company;
import java.util.Comparator;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
//比较器
//默认情况下,会调用对象的compareTo进行比较
// 如果传入了比较器,就会使用传入的比较器进行比较
//不穿参数使用的默认是字符串中的CompareTo
TreeSet<String>set=new TreeSet<>();
set.add("aaaaaa");
set.add("z");
set.add("wc");
set.add("myx");
set.add("cba");
System.out.println(set);//[aaaaaa, cba, myx, wc, z]
//使用比较器进行比较
//实现一个类来去实现这个接口
//覆盖它里面的方法
System.out.println("--------------------------");
TreeSet<String>set2=new TreeSet<>(new CompareLength());//传入比较器的话,就不会调用字符串中的compare进行比较
set2.add("aaaaaa");
set2.add("z");
set2.add("wc");
set2.add("myx");
set2.add("cba");
System.out.println(set2);//
}
}
class CompareLength implements Comparator<String>{
@Override
//1第一个参数代表当前正在添加的对象
//2代表集合当中的对象
public int compare(String o1, String o2) {
int length = o1.length() - o2.length();
return length==0?o1.compareTo(o2):length;
}
}