集合

集合的概念

首先, 我们来聊一聊数组, 我们知道, 数组是同一类元素的有序集合, 但是数组却又一个致命的缺点, 那就是数组一旦被定义, 那就无法再对其容量进行改变了, 但是我们的开发或者日常生活中, 经常要存储一些可变的数据集合, 这时数组便不能满足我们的需要, 我们就需要一些能够动态增长长度的容器来保存我们的数据。而我们需要对数据的保存的各种逻辑可能可能是多种多样的,于是就有了各种各样的数据结构。

Java中对于各种数据结构的实现,就是我们用到的集合。

集合API

Java的集合类(集合API,集合框架)是Java数据结构的实现,是由很多的接口、抽象类、具体类组成的,都位于java.util包中。

集合的体系图如下:

java数组套map java 数组api_java

Collection接口

Collection接口–定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。

Set中的数据对象没有顺序且不可以重复(无序,不重复)。

List中的数据对象有顺序且可以重复(有序且重复)。

Collection中定义的集合共有方法:

返回值类型方法名功能booleanadd(Object element)添加元素

booleanaddAll(Collection c)将制定集合的元素添加到此集合中

booleanremove(Object element)删除指定元素

booleanremoveAll(Collection c)删除指定集合 不包含时,删除相交的元素

voidclear()清空集合

intsize()集合内的元素个数

booleanisEmpty()判断集合是否为空

booleancontains(Object element)判断集合中是否包含指定元素

booleancontainsAll(Collection c)判断是否包含某个集合

booleanretainAll(Collection c)求交集,集合数据发生变化返回true,不变返回false

default booleanremoveIf()条件删除

注意:这里有一个需要注意的地方,使用其添加方法添加基本数据类型时会默认调用valueof()方法装箱,因为**集合只能存储引用数据类型。**例如:

import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
Collection data = new ArrayList();
//集合只能存储引用类型
data.add("hello!");
//添加基本类型都会将基本类型装箱为引用类型
data.add(1);//这里添加1会,将1转换为Integer类型
data.add("World");
data.remove(1);
System.out.println(data);
}
}

代码示例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
//Collection中的共有的方法
public class Preview1 {
public static void main(String[] args) {
Integer [] arr1 = {1,2,3};
Collection arr = new ArrayList<>(20);
for (int i = 0; i<15;i++){
arr.add(i);
}
/*
boolean add(Object element);
boolean addAll(Collection c);
boolean remove(Object element);
boolean removeAll(Collection c);删除指定集合 不包含时,删除相交的元素
void clear();
int size();
boolean isEmpty();
boolean contains(Object element);
boolean containsAll(Collection c);
boolean retainAll(Collection c); 求交集,集合数据发生变化返回true,不变返回false
*/
System.out.println(arr.add(100));//true
System.out.println(arr);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 100]
System.out.println(arr.addAll(Arrays.asList(arr1)));
System.out.println(arr);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 100, 1, 2, 3]
System.out.println(arr.remove(1));//[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 100, 1, 2, 3]
System.out.println(arr);
System.out.println(arr.removeAll(Arrays.asList(arr1)));
System.out.println(arr);//[0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 100]
System.out.println(Arrays.toString(arr1));//[1, 2, 3]
// arr.clear();
// System.out.println(arr);//[]
System.out.println(arr.size());//13
System.out.println(arr.isEmpty());//false
System.out.println(arr.contains(10));//true
System.out.println(arr.containsAll(Arrays.asList(arr1)));//true
System.out.println(arr.retainAll(Arrays.asList(arr1)));//true
//removeIf() 条件删除
System.out.println(arr.removeIf(new Predicate() {
@Override
public boolean test(Integer integer) {
return integer.equals(0);
}
}));
System.out.println(arr);
}
}

List接口

有序:按照添加顺序排列。

可以有重复元素。

List继承了Collection接口, 有三个实现的类:

ArrayList: 数组列表, 数据采用数组方式存储

LinkedList: 链表

Vector: 数组列表, 添加了同步锁, 线程安全

List接口的常用方法

List接口的方法在它的实现类中都可以使用。(Collection中定义的方法这里都可以使用)

返回值方法名功能voidadd(Object obj)将Object类型的元素添加到集合末尾

voidadd(int index, Object obj)将Object类型的元素添加到指定位置
voidaddAll(Collection c)给集合添加一个新集合,也可以加上位置
voidremove(int index)删除指定位置的元素
voidremove(Object obj)删除指定元素(集合中第一次出现)
voidremoveAll(Collection c )删除指定集合,不包含时删除相交得的元素
set(int index,Object o)将指定索引的元素替换为指定的元素
get(int index)返回指定索引位置的元素
booleancontains(Object o)判断集合中是否包含指定元素
booleancontainsAll(Collection c)判断集合中是否包含指定集合
intindexOf(Object o)返回指定元素第一次出现的索引值,没有就返回-1
booleanisEmpty()判断集合是否为空
intlastIndexOf(Object)返回元素最后一次出现的索引值
intsize()返回此集合的元素数量
ListsubList(int formIndex,int toIndex)截取指定区间返回一个集合
voidclear( )清空集合
asList, toArray(), toArray(T[] a) 的用法
import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class Preview1 {
private static Object String;
public static void main(String[] args) {
String [] arr = {"1","2","3","4"};
//asList--->Arrays中的方法,可以将一个数组转化为集合
Collection list = new ArrayList();
System.out.println(list.addAll(Arrays.asList(arr)));
System.out.println(list);
//toArray()--->返回一个类型为Object[]的包含集合中的所有元素的数组
Object [] arr1 = list.toArray();
System.out.println(Arrays.toString(arr1));
//返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型
String[] arr2 = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(arr2));
}
}

代码示例

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
//List接口的常用方法
public class ListDemo {
public static void main(String[] args) {
List list = new LinkedList();
//添加元素---末尾
list.add("中");
list.add(1);
list.add(true);
System.out.println(list);
//添加元素到指定位置
list.add(1,"风");
//添加一个新集合
Collection c = new LinkedList();
c.add(1);
c.add(2);
c.add(3);
list.addAll(c);
System.out.println(list);
//添加一个新集合到指定位置
list.addAll(2,c);
System.out.println(list);
//删除指定位置元素
list.remove(1);
System.out.println(list);
//删除指定元素
list.remove(1);
System.out.println(list);
//修改指定位置元素---返回这个位置之前的元素
System.out.println(list.set(3,"天"));
//查询指定位置元素
System.out.println(list.get(3));
//判断集合送是否包含指定元素
System.out.println(list.contains("天"));
//判断是否包含指定集合
System.out.println(list.containsAll(c));
//返回元素第一次出现的索引值,没有就返回-1
System.out.println(list.indexOf("风"));
//判断集合是否为空
System.out.println(list.isEmpty());
//返回元素最后出现一次的索引值
System.out.println(list.lastIndexOf(3));
//返回集合中的元素数量
System.out.println(list.size());
//截取集合中的指定区间
System.out.println(list.subList(2,5));
//清空集合
list.clear();
}
}

实现类:ArrayList

ArrayList是实现了长度可变的数组, 在内存中分配连续的空间。

**优点:**底层数据结构是数组,查询快,增删慢,遍历元素和随机访问元素的效率高。

**缺点:**线程不安全。

ArrayList的常用方法(list接口中的方法在ArrayList中都可用):

返回值方法名功能voidensureCapacity(int minCapacity)自行增加容量,以保证它最少有指定数量的元素

protected voidremoveRange(int f,int e)删除指定区间的元素,需要继承ArrayList才能使用

booleanretainAll(Collection c)仅保留此集合中在包含指定集合的元素

voidtrimToSize()修改当前集合的容量大小为元素个数数量

voidsort(比较器)需要传入一个比较器进行排序

代码示例

import java.util.ArrayList;
import java.util.Comparator;
public class ArrayListDemo extends ArrayList {
public static void main(String[] args) {
ArrayList list = new ArrayList();
//添加元素---末尾
list.add("中");
list.add(1);
list.add(true);
System.out.println(list);
//添加元素到指定位置
list.add(1, "风");
//添加一个新集合
ArrayList c = new ArrayList();
c.add(1);
c.add(2);
c.add(3);
list.addAll(c);
System.out.println(list);
//自行增加容量---让其有最少为指定的容量
list.ensureCapacity(20);
//删除指定区间,这是一个受保护权限,需要继承ArrayList才能在子类中调用
ArrayListDemo list1 = new ArrayListDemo();
list1.add(0);
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
list1.removeRange(1,4);
System.out.println(list1);
//仅保留此集合中包含在指定集合中的元素
list.retainAll(c);
System.out.println(list);
//sort 传入一个比较器进行排序
ArrayList list2 = new ArrayList<>();
list2.add(3);
list2.add(7);
list2.add(5);
list2.add(0);
list2.add(1);
list2.sort(new pd());
System.out.println(list2);
}
}
class pd implements Comparator{
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}

实现类:LinkedList

LinkedList采用链表存储的方式。插入、删除元素时效率较高。

优点:底层数据结构使用的是链表,查询快、增删慢。

缺点:线程不安全。

LinkedList的常用方法和ArrayList的差不多,也都实现了List接口和Collection接口,底层以链表的形式存储

代码示例

import java.util.*;
public class LinkedListDemo extends LinkedList{
public static void main(String[] args) {
LinkedList list = new LinkedList();
//添加元素---末尾
list.add("中");
list.add(1);
list.add(true);
System.out.println(list);
//添加元素到指定位置
list.add(1,"风");
//添加一个新集合
Collection c = new LinkedList();
c.add(1);
c.add(2);
c.add(3);
list.addAll(c);
System.out.println(list);
//添加一个新集合到指定位置
list.addAll(2,c);
System.out.println(list);
//删除指定位置元素
list.remove(1);
System.out.println(list);
//删除指定元素
list.remove(1);
System.out.println(list);
//修改指定位置元素---返回这个位置之前的元素
System.out.println(list.set(3,"天"));
//查询指定位置元素
System.out.println(list.get(3));
//判断集合送是否包含指定元素
System.out.println(list.contains("天"));
//判断是否包含指定集合
System.out.println(list.containsAll(c));
//返回元素第一次出现的索引值,没有就返回-1
System.out.println(list.indexOf("风"));
//判断集合是否为空
System.out.println(list.isEmpty());
//返回元素最后出现一次的索引值
System.out.println(list.lastIndexOf(3));
//返回集合中的元素数量
System.out.println(list.size());
//截取集合中的指定区间
System.out.println(list.subList(2,5));
//删除指定区间,这是一个受保护权限,需要继承ArrayList才能在子类中调用
LinkedListDemo list1 = new LinkedListDemo();
list1.add(0);
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
list1.removeRange(1,4);
System.out.println(list1);
//sort 传入一个比较器进行排序
ArrayList list2 = new ArrayList<>();
list2.add(3);
list2.add(7);
list2.add(5);
list2.add(0);
list2.add(1);
list2.sort(new pd());
System.out.println(list2);
}
}
class pd implements Comparator {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}

实现类:Vector

Vector底层实现就是数组,和ArrayList是一致的,但是Vector添加了同步锁synchronized,保证了线程安全。

效率测试

测试ArrayList和LinkedList的存入和查找效率

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
//测试ArrayList和LinkedList存,查效率
public class Test1 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
Date date1 = new Date();
for (int i = 0; i < 10000; i++) {
list.add("a");
}
for (int i = 0; i < 90000; i++) {
list.add(2500,"a");
}
Date date2 = new Date();
System.out.println("ArrayList存10万次的时间:"+(date2.getTime()-date1.getTime()));
// ArrayList存10万次的时间:521ms
Date date3 = new Date();
for (int i = 0; i < 100000; i++) {
list.get(2500);
}
Date date4 = new Date();
System.out.println("ArrayList查10万次的时间:"+(date4.getTime()-date3.getTime()));
//ArrayList查10万次的时间:0ms
LinkedList list1 = new LinkedList();
Date date5 = new Date();
for (int i = 0; i < 10000; i++) {
list1.add("a");
}
for (int i = 0; i < 90000; i++) {
list1.add(2500,"a");
}
Date date6 = new Date();
System.out.println("LinkedList存10万次的时间:"+(date6.getTime()-date5.getTime()));
//LinkedList存10万次的时间:399ms
Date date7 = new Date();
for (int i = 0; i < 100000; i++) {
list1.get(2500);
}
Date date8 = new Date();
System.out.println("LinkedList查10万次的时间:"+(date8.getTime()-date7.getTime()));
//LinkedList查10万次的时间:401ms
}
}

List接口的迭代

for循环

语法格式:for(int i;i < list.size() ; i++){…}

注意: for循环有一个缺陷,就是在删除元素的时候,集合的长度会变小,但是索引 i 的值却在不断变大,这就会出现删不尽的情况,因此我们总是需要做一些小小的处理,请看代码。

代码示例

import java.util.ArrayList;
//遍历数组--for循环
public class ForDemo {
public static void main(String[] args) {
ArrayList arr = new ArrayList<>();
arr.add("a");
arr.add("s");
arr.add("d");
arr.add("e");
arr.add("s");
System.out.println(arr);
for (int i = 0; i < arr.size(); i++) {
System.out.println(arr.get(i));
}
//删除元素后,集合的长度也发生了变化,但是索引 i 的值一直在变大,会出现删不尽的情况
for (int i = 0; i < arr.size(); i++) {
arr.remove(i);
//使用 i-- 将i的值不断变小,等于说每一次都在删除敌意个数
i--;
}
System.out.println(arr);//[s, e]
}

增强for循环

语法格式:for(Object obj:list){System. out. println(obj);}

**注意:**增强for循环,不允许在遍历的时候对元素进行操作,否则会抛出异常,但是我们可以进行一次操作,比如删除一个元素后立刻break,趁着jvm还没反应过来抛出异常时,我们就跳出去。

代码示例

import java.util.ArrayList;
//迭代---增强for循环
public class ForeachDemo {
public static void main(String[] args) {
ArrayList arr = new ArrayList<>();
arr.add("a");
arr.add("s");
arr.add("d");
arr.add("e");
arr.add("s");
System.out.println(arr);
for (String s: arr) {
System.out.println(s);
}
//增强for循环,不允许遍历时对元素进行操作,否则会报错:ConcurrentModificationException
//但是可以进行一次操作,比如删除已次元素后,趁着异常还没抛出时立刻跳出
for (String s: arr) {
arr.remove(s);
break;
}
System.out.println(arr);//[s, d, e, s]
}
}

迭代器–iterator

这是Collection接口中提供的操作,直接上代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Preview2 {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator iterator = arr.iterator();
//hasNext() 检测迭代器中是否还有元素
while(iterator.hasNext()){
//每次都从arr中拿出一个元素
String s = iterator.next();
if (s.equals("c")){
//这里不能使用原本的集合对象,因为迭代器中不能直接调用集合对象,需要使用iterator作为中间人
iterator.remove();
}
}
System.out.println(arr);
}
}