集合框架概述
一方面面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储
另一方面使用Array存储对象具有一些弊端,而Java集合就像一种容器,可以动态地把多个对象的引用放入容器中
集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)
数组存储的特点
- 数组初始化以后,长度就确定了
- 数组声明的类型,就决定了进行元素初始化时的类型
比如:String[] arr; int[] arr1; Object[] arr2;
数组存储的弊端
- 一旦初始化以后长度就不可变了,不便于扩展
- 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高
- 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
- 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足
Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
集合的使用场景
集合的分类
Java集合可分为Collection和Map两种体系
- Collection接口:单列数据,定义了存取一组对象的方法的集合
- List:元素有序、可重复的集合
- Set:元素无序、不可重复的集合
- Map接口:双列数据,保存具有映射关系“key-value对”的集合
集合框架结构
Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据 -->“动态”数组
|----ArrayList:线程不安全的,查询效率高,底层采用Object[] elementData数组存储
|----LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList效率高底层采用双向链表存储
|----Vector:作为List的古老实现类,线程安全的,效率低,底层采用Object[]数组存储
|----Set接口:存储无序的、不可重复的数据 -->数学概念上的“集合”
|----HashSet:作为Set接口主要实现类,线程不安全,可以存null值
|----LinkedHashSet:HashSet的子类,遍历内部数据时,按照添加顺序遍历
遍历操作LinkedHashSet效率高于HashSet
|----TreeSet:可以按照添加对象的指定属性,进行排序
Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)
|----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
|----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。
原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap。
|----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用红黑树
|----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
|----Properties:常用来处理配置文件。key和value都是String类型
Collection接口
Collection接口是List、Set、Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和 Queue集合
- JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如Set和List)实现
- 在Java5之前,集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型处理
从Java5之后,增加了泛型以后,Java集合可以记住容器中对象的数据类型
常用方法
获取元素个数
**int size()**
获取元素个数
添加**boolean add(Object obj)**
**boolean addAll(Collection c)**
删除**void clear()**
清空集合**boolean remove(Object o)**
会通过equals方法判断是否是要删除的元素,只会删除找到的第一个元素**boolean removeAll(Collection c)**
删除两容器都包含的元素
判断**boolean isEmpty()**
**boolean contains(Object o)**
会通过元素的equals方法来判断是否是同一个对象**boolean containsAll(Collection c)**
判断是否都包含c中元素
交集**boolean retainAll(Collection c)**
把交集的结果存在当前的集合中,不影响c
集合是否相等**boolean equals(Object obj)**
比较俩集合是否相同
转换成对象数组**Object [] toArray()**
反过来是**Arrays.asList(T... a)**
**Object [] toArray(T[] a)**
获取集合对象的哈希值**hashCode()**
遍历**Iterator<E> iterator()**
返回迭代器对象,用于集合遍历 ```java @Test public void test(){ Collection coll = new ArrayList();// add(Object e):将元素e添加到集合coll中 coll.add(“AA”); coll.add(“BB”); coll.add(123);//自动装箱 coll.add(new Date());
// size():获取添加的元素的个数 System.out.println(coll.size()); // 4
// addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add(“CC”);
coll.addAll(coll1); System.out.println(coll.size()); // 6 System.out.println(coll);
// clear():清空集合元素 coll.clear();
// isEmpty():判断当前集合是否为空 System.out.println(coll.isEmpty()); }
@Test public void test1(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person(“Jerry”, 20)); coll.add(new String(“Tom”)); coll.add(false);
// 1.contains(Object obj):判断当前集合中是否包含obj
// 我们在判断时会调用obj对象所在类的equals()
boolean contains = coll.contains(123);
System.out.println(contains);
System.out.println(coll.contains(new Person("Jerry",20))); // true
// 2.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。
Collection coll1 = Arrays.asList(123,4567);
System.out.println(coll.containsAll(coll1));
}
@Test public void test2(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person(“Jerry”,20)); coll.add(new String(“Tom”)); coll.add(false);
// 3.remove(Object obj):从当前集合中移除obj元素
coll.remove(1234);
System.out.println(coll);
coll.remove(new Person("Jerry",20));
System.out.println(coll);
// 4. removeAll(Collection coll1):差集:从当前集合中移除coll1中所有的元素。
Collection coll1 = Arrays.asList(123,456);
coll.removeAll(coll1);
System.out.println(coll);
}
@Test public void test3(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person(“Jerry”,20)); coll.add(new String(“Tom”)); coll.add(false);
// 5.retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
Collection coll1 = Arrays.asList(123,456,789);
coll.retainAll(coll1);
System.out.println(coll);
// 6.equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同。
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add(123);
coll1.add(new Person("Jerry",20));
coll1.add(new String("Tom"));
coll1.add(false);
System.out.println(coll.equals(coll1));
// 7.hashCode():返回当前对象的哈希值
System.out.println(coll.hashCode());
}
**集合与数组间的转换**
```java
// 8.集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 拓展:数组 --->集合:调用Arrays类的静态方法asList(T... a)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size()); //1 🔴基本类型数组被当成了一个元素
List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size()); //2 引用类型数组不会
//Arrays中asList方法
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a); // 这个ArrayList是Arrays的私有静态内部类
}
// 9.iterator():返回Iterator接口的实例,用于遍历集合元素。放在IteratorTest.java中测试
说明:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
补充
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(343);
coll.add(343);
coll.forEach(System.out::println); // Iterable接口的default方法,用于遍历
}
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
Iterator与foreach
遍历Collection的两种方式
- 使用迭代器Iterator
- foreach循环(或增强for循环)
迭代器接口Iterator
java.utils.IteratorIterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素 GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公交车上的售票员”、“火车上的乘务员”、“空姐”
创建
- Collection接口继承了java.lang.Iterable接口
- Iterable接口有一个iterator()方法,用以返回一个实现了Iterator接口的对象
- 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
作用
- Iterator仅用于遍历集合,Iterator本身并不提供承装对象的能力
如果需要创建Iterator对象,则必须有一个被迭代的集合
- 在调用
**next()**
方法之前必须要调用**hasNext()**
进行检测
若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
Iterator iterator = coll.iterator();
// 方式一:
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// //报异常:NoSuchElementException
// System.out.println(iterator.next());
// 方式二:不推荐
// for(int i = 0; i < coll.size(); i++) {
// System.out.println(iterator.next());
// }
// 方式三:推荐
// hasNext():判断是否还有下一个元素
while(iterator.hasNext()) {
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
}
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//错误方式一:❌
// Iterator iterator = coll.iterator();
// while((iterator.next()) != null){
// System.out.println(iterator.next());
// }
//错误方式二:❌
//集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
while (coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
}
iterator中的remove()方法
- 内部定义了remove(),可以在遍历的时候删除集合中的元素。此方法不同于集合直接调用remove()
未调用next()直接remove()或连续两次调用remove()都会报IllegalStateException
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//删除集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()) {
// iterator.remove();
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
}
}
//遍历集合
iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
foreach——JDK5新特性
Java5提供了foreach循环迭代访问集合和数组
遍历操作不需获取Collection或数组的长度,无需使用索引访问元素
foreach的底层调用Iterator完成操作 ```java @Test public void test1() { Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person(“Jerry”,20)); coll.add(new String(“Tom”)); coll.add(false);
// for(集合元素的类型 局部变量 : 集合对象) // 内部仍然调用了迭代器。 for(Object obj : coll) {
System.out.println(obj);
} }
// 遍历数组 @Test public void test2() { int[] arr = new int[]{1,2,3,4,5,6}; // for(数组元素的类型 局部变量 : 数组对象) for(int i : arr) { System.out.println(i); } }
**练习**
```java
@Test
public void test3(){
String[] arr = new String[]{"MM","MM","MM"};
// 方式一:普通for赋值 会改
for (int i = 0; i < arr.length; i++) {
arr[i] = "GG";
}
// 方式二:增强for循环不改原值
for(String s : arr){
s = "GG";
}
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}