Java中的集合体系之Collection接口(上)
一、引入
1.集合,数组都是对多个数据进行存储操作的结构,简称为JAVA容器。此时说的存储,主要指的是内存层面上,不涉及到持久化的存储。
2.1数组存储数据的特点:
1.数组一旦初始化之后,长度就确定了
2.数组一旦定义好之后,它的元素类型也就确定了,我们也就只能操作指定类型的数据了,例如:String[] arr1; int[] arr2; Object[] arr3;
2.2数组存储数据的缺点:
1.一旦初始化之后,其长度不可修改。
2.数组中提供的方法非常有限,对于增删改查等操作,非常不方便,同时效率也不高。
3.获取数组中实际元素的个数的需求,数组没有现成的属性或者方法可用
4.数组存储数据的特点:是有序,可重复的;所以对于无序不可重复的需求不能
满足。
二、java中的集合体系
1.Collection接口
位于java.util包下的Collection接口。单列集合,存储单列数据,用来存储一个一个的对象。
1)List子接口:存储有序的,可重复的数据(也称之为:动态数组)
①ArrayList:是List接口的实现类,底层存储是数组,线程不安全,效率高
②LinkedList:是List接口的实现类,底层存储是双向链表,线程不安全,效率高
③Vector:是List接口的实现类,底层存储是数组,线程安全,效率低
2)Set子接口:存储的是无序的,不可重复的数据(符合高中的集合)
①HashSet:是Set接口的实现类,使用hash表(数组)存储元素
②LinkedHashSet:是Set接口的实现类,链表维护元素的插入次序
③TreeSet:是Set接口的实现类,用来排序
package com.zretc.java;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.junit.Test;
public class CollectionTest {
@Test
public void methodTest1() {
System.out.println("methodTest1:");
//首先:通过多态的方式创建Collection引用的集合
Collection col1 = new ArrayList();
//1.int size();返回集合中的个数
System.out.println(col1.size() + "个元素");//0个元素
//2.boolean add(Object obj);向集合中添加一个元素,存储的是Object
//类型(在没有添加泛型的时候)
//注意:当确认集合泛型的时候,存储的是确认后的泛型类型
col1.add("张三");
col1.add(12);
col1.add(new Person("Amy",11));
//测试:此时集合内的元素个数。
System.out.println(col1.size() + "个元素");//3个元素
System.out.println(col1.toString());//集合中重写了toString方法,输出[张三, 12, Person [name=Amy, age=11]]
//3.boolean addAll(Collection col):将形参中的集合中的所有元素添加
//到当前集合中
Collection col2 = new ArrayList();
col2.addAll(col1);
col2.add("第二个集合");
System.out.println(col2);//[张三, 12, Person [name=Amy, age=11], 第二个集合]
System.out.println(col2.size() + "个元素");//4个元素
//4.boolean isEmpty():判断当前集合是否为空
System.out.println(col1.isEmpty());//false 不为空
//5.void clear();清空当前集合
col1.clear();
System.out.println(col1.isEmpty());//当前集合为空,返回true
System.out.println();
}
@Test
public void methodTest2() {
System.out.println("methodTest2:");
Collection col1 = new ArrayList();
Date date = new Date();
col1.add(123);
col1.add("aa");
col1.add(new String("bb"));
col1.add(date);
col1.add(new Person("tony",2));
//6.boolean contains(Object obj);判断集合中是否包含某元素,若包含
//返回true,否则返回false
//注意1:该方法的判断依据是所在类的equals方法
//注意2:如果集合中存入的元素是自定义类,要求自定义类的equals方法必
//须重写,
//否则会按照Object类中的equals方法执行。
boolean b = col1.contains(123);//true
b = col1.contains("aa");//true
b = col1.contains(new String("bb"));//ture
b = col1.contains(new Person("tony",2));//false
System.out.println(b);
//7.boolean containsAll(Collection col):判断是否包含col集合
Collection col2 = new ArrayList();
col2.add(123);
col2.add("aa");
col2.add(456);
//col1集合中是否包含col2集合中的全部元素
System.out.println(col1.containsAll(col2));//false
//8.boolean retainAll(Collection col):
//返回当前集合和col集合的公共部分的元素,返回给当前集合
System.out.println(col1);//[123, aa, bb, Wed Jan 26 14:52:43 CST 2022, Person [name=tony, age=2]]
System.out.println(col2);//[123, aa, 456]
col1.retainAll(col2);
System.out.println(col1);//[123, aa]
//9.boolean remove(Object o):删除集合中的某个元素,根据equals删
boolean b1 = col1.remove(new Person("tony",2));
System.out.println(b1);//false
System.out.println(col1);//[123, aa]
//10.boolean removeAll(Collection col);从当前集合中删除包含在col
//中的元素
System.out.println(col2);//[123, aa, 456]
col1.removeAll(col2);
System.out.println(col1);//[]
System.out.println();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void methodTest3() {
System.out.println("methodTest3:");
Collection col1 = new ArrayList();
Date date = new Date();
col1.add(123);
col1.add("aa");
col1.add(new String("bb"));
col1.add(date);
col1.add(new Person("tony",2));
//11.boolean equals(Object obj):判断两个集合中的元素是否相同
Collection col2 = new ArrayList();
col2.add(123);
col2.add("aa");
col2.add(new String("bb"));
col2.add(date);
col2.add(new Person("tony",2));
System.out.println(col1.equals(col2));//false
//12.hashCode():返回当前对象的哈希值,一串地址。
System.out.println(col1.hashCode());//941605507
//小扩展:将一个数字转换为一个十六进制的字符串
System.out.println(Integer.toHexString(col1.hashCode()));//381fc283
//13.集合--->数组 object[] toArray();
Object[] objs = col1.toArray();
for(int i = 0; i< objs.length;i++) {
System.out.println(objs[i]);
}
// 123
// aa
// bb
// Wed Jan 26 15:07:05 CST 2022
// Person [name=tony, age=2]
//小扩展:数组--->集合 Arrays.asList()
String[] strs = {"AA","BB","CC","DD"};
List<String> list = Arrays.asList(strs);
System.out.println(list);//[AA, BB, CC, DD]
List list2 = Arrays.asList(new int[] {1,2,3});
System.out.println(list2);//结果是一个地址值 [[I@46ee7fe8]
List list3 = Arrays.asList(new Integer[] {1,2,3});
System.out.println(list3);//[1,2,3]
}
}
class Person {
private String name;
private Integer age;
public Person() {
super();
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
2.Collection集合遍历操作
1)Iterator接口
①Iterator接口概述 :Iterator对象称为迭代器,主要用来遍历Collection集合中的元素,Map接口不适用;
②Collection接口继承了java.lang.Iterable接口,该接口有一个iterator(),该方法返回一个迭代器的对象,那么就是证明所有Collection接口的实现类都拥有这个iterator()。
package com.zretc.java;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.junit.Test;
public class IteratorTest {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void test1() {
Collection col1 = new ArrayList();
col1.add(123);
col1.add("aa");
col1.add(new String("bb"));
col1.add(new Person("tony", 2));
//1.创建迭代器对象。
Iterator iterator = col1.iterator();
// System.out.println(iterator.next());123
// System.out.println(iterator.next());aa
// System.out.println(iterator.next());bb
// System.out.println(iterator.next());Person [name=tony, age=2]
//方式1:不推荐
for (int i = 0; i < col1.size(); i++) {
System.out.println(iterator.next());
}
//方式2:推荐使用方式
//①hasNext():判断是否有下一个元素
while (iterator.hasNext()) {
//②next():指针下移;将下移后的集合位置上的元素返回
System.out.println(iterator.next());
// 123
// aa
// bb
// Person [name=tony, age=2]
}
}
}
2)Iterator的执行原理
第一步:使用iterator.hasNext()来确认是否有下一个元素
第二步:确认有元素之后,通过iterator.next()干两件事
①指针下移
②将指针指向的位置的元素进行返回
3)Iterator常见错误写法
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void teat2() {
Collection col1 = new ArrayList();
col1.add(123);
col1.add("aa");
col1.add(new String("bb"));
col1.add(new Person("tony", 2));
//第一种错误写法:结果跳着显示,因为该while代码块调用两次iterator.next()
// aa
// Person [name=tony, age=2]
// Iterator iterator = col1.iterator();
// while(iterator.next() != null) {
// System.out.println(iterator.next());
// }
//第二种错误写法:
//集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认的指针都在集合的第一个元素之前。
while (col1.iterator().hasNext()) {//死循环
System.out.println(col1.iterator().next());// 每次都输出第一个元素
}
}
4)remove()方法
Iterator内部定义了remove()方法,可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove(),
在没有调用next()之前,就先remove,报异常IllegalStateException。已经调用完一次remove方法之后,再调用就会报错
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void test3() {
Collection col1 = new ArrayList();
col1.add(123);
col1.add("aa");
col1.add(new String("bb"));
col1.add(new Person("tony", 2));
Iterator iterator = col1.iterator();
while (iterator.hasNext()) {
//在没有调用next()之前,就先remove,报异常IllegalStateException。
// iterator.remove();
Object obj = iterator.next();
if("aa".equals(obj)) {
iterator.remove();
//已经调用完一次remove方法之后,再调用就会报错
iterator.remove();//报错
}
}
//遍历集合
iterator = col1.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
5)foreach增强for循环
JDK5.0新增foreach循环,用于遍历数组和集合
语法:
执行流程:
自动去取集合对象的元素,先取第一个值,赋值给局部变量,然后输出局部变量,然后再取第二个值,再赋值给局部变量,然后输出局部变量……
注意:
当我们debug的时候,进去到foreach循环中,发现其实调用的还是迭代器。
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void test1() {
Collection<Integer> col1 = new ArrayList<Integer>();
col1.add(123);
col1.add(456);
col1.add(789);
col1.add(333);
col1.add(444);
//使用增强for循环
//形式上虽然是增强for循环,但是内部仍然调用了迭代器
for(Integer i : col1) {
System.out.println(i);
}
// 123
// 456
// 789
// 333
// 444
//当一个集合元素没有泛型规定时,传入的集合数据类型是Object
//for(Object obj : col1) {
// System.out.println(obj);
//}
}
6)list接口的特点
(1)是Collection子接口之一,是JDK1.2的时候出现的;
(2)鉴于java中数组存储数据的局限性,我们通常使用List来代替数组,我们称之为动态数组。‘
(3)List集合中的元素都是有序,且可重复的,集合中的每个元素都有其对应的顺序索引。
(4)JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector
三个实现类的区别:
①相同点:三个类都是实现了List接口,存储数据的特点相同:存储有序的,可重复的数据
②不同点:
ArrayList:在JDK1.2的时候出现,作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elementData进行存储。
LinkedList:在JDK1.2的时候出现,对于频繁的插入,删除的操作,效率会高
于ArrayList;底层是使用双向链表进行存储。
Vector:在JDK1.0的时候出现,作为List接口的古老实现类,线程安全的,但是效率低,底层使用Object[] elementData存储数据。
7)双向链表底层是如何存储的?
(1)使用双向链表存储—插入操作
(2)使用双向链表存储—删除操作
8)底层区别
(1)ArrayList,查找,加入,遍历更适合ArrayList。
底层原理:JDK1.8当创建对象的时候,底层Object[] elementData初始化为{},并没有像JDK1.7的时候创建长度为10的数组,而是在.add添加元素的时候,底层创建了一个长度为10的数组,并且将值添加到elementData[0]。当继续添加元素,使得原本的数组容量不够的时候,就扩容;默认情况是:扩容为原来的1.5倍,同时需要将原有数组复制到新的数组中。之
前的数组就会被废弃。
注意:使用无参构造不断扩容会导致程序效率低,如果知道要存储到集合的数据大致
的数量,可以直接使用单参构造;当创建对象的时候,直接给你对应的数据空间,效
率会高一点。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity:
"+
initialCapacity);
}
}
(2)LinkedList,插入,删除操作更适合LinkedList
底层原理:内部声明了Node类型的first和last属性,默认值是null;当集合对象.add(123),会将123封装到Node中,创建Node对象;其中Node定义体现了LinkedList双向链表的说法。
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
(3)Vector,JDK1.7&JDK1.8通过Vector()构造器创建对象的时候,底层都会创建长度为10的数组在扩容方面,默认扩容为原来数组长度的2倍。