1、什么是集合?有什么用?
数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。
集合为什么说在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展出来。
//不能放基本类型,可以存放object类型(即只能存放对象的引用)
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
//数组也是内存对象
//ArrayList 查询效率高,增删差
//LinkedList 增删效率高,查找效率低
//为null说明没对象
//为空说明有对象但是没赋值
//remove()方法会自动调用该对象的equals方法
//Collection是父类,所以里面的方法是子类都有的
public class Collection_1 {
public static void main(String[] args) {
//创建集合对象
Collection c1 = new ArrayList();
//判断是否为空即个数是否为0
System.out.println(c1.isEmpty());//true
// 集合中是不能保存基本类型的,需要转换为对应包装类才可以
// 这里会进行自动装箱为Integer类型,然后发生多态转型为Object类型 进行存储
c1.add(213);
c1.add(new Integer(1));
c1.add(new Collection_1());
// 个数
System.out.println(c1.size());//3
//是否为空
System.out.println(c1.isEmpty());//false
// 删除,会调用要删除元素的equals方法,但是Integer覆写了,所以可以把1删掉
c1.remove(1);
A a = new A("李四");
A a1 = new A("李四");
c1.add(a);
// 所以使用集合保存自定义类型的时候,要注意,是否要覆写equals方法,定义怎么算相等
c1.remove(a1);
Object[] arr = c1.toArray();
for (Object object: arr){
System.out.println(object);//213
//Mouth_1_.Week_3_.Day_18._1_Collection.Collection_1@1540e19d
}
// 清空集合
c1.clear();
System.out.println(c1.size());//0
}
}
class A {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public A() {
}
public A(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
A a = (A) o;
return Objects.equals(name, a.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String toString() {
return "A{" +
"name='" + name + '\'' +
'}';
}
}
2、集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
list.add(100) ; // 自动装箱Integer
注意:
集合在java中本身是一个容器,是一个对象。
集合中任何时候存储的都是 “引用”。
3、集合内存图示
4、在Java中每一个不同的集合,底层都会对应不同的数据结构
往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。
什么是数据结构?
数据存储的结构就是数据结构。
不同的数据结构,数据存储方式不同
例如:new ArrayList();
创建一个集合,底层是数组。new LinkedList();
创建一个集合对象,底层是链表。new TreeSet();
创建一个集合对象,底层是二叉树。
5、集合在JDK的哪个包下?
在java.util.*
包下;
所有的集合类和集合接口都在java.util
包下。
6、在Java中集合分为两大类
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:
java.util.Collection;
一类是以键值对的方式存储元素:
以键值对的方式存储元素,这一类集合中超级父接口:
java.util.Map;
collection方法
7.集合的继承结构图
8.Map的继承层次结构
9.所有实现类总结
■ ArrayList:底层是数组。
■ LinkedList:底层是双向链表。
■ Vector:底层是数组,线程安全的,效率较低,使用较少。
■ HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了。.
■ TreeSet: 底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分了。
■ HashMap:底层是哈希表。线程不安全。
■ Hashtable:底层也是哈希表,只不过线程安全的,效率较低,使用较少。
■ Properties: 是线程安全的,并且key和value只能存储字符串String。
■ TreeMap: 底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序。。
1、List集合存储元素的特点:
有序可重复。
有序:存进去的顺序和取出的顺序相同,每一个元素都有下标。
可重复:存进去1,可以再存储一个1。
2、Set集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出的顺序不一-定相同。 另外Set集合中元素没有下标。
不可重复:存进去1,不能再存储1了。
3、SortedSet (SortedMap) 集合存储元素特点:
首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能再存储1了。
可排序:可以按照大小顺序排列。
Map集合的key,就是一个set集合。
往set集合中放数据,实际上到了Map集合的key部分。
迭代器iterator
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* 在面向对象编程里 迭代器模式是一种设计模式,是一种最简单也是最常见的设计模式
* 它可以让用户透过特定的接口巡防容器中的每个元素,而不用了解底层的实现
* 获取迭代器对象
* Iterator IT = 集合对象 .iterator();
* 包括三个方法:
* 1.boolean hasNext()判断下面还有没有元素,
* 如果有就返回true,反之返回false
* 2.E next():获取下一个元素并指向下一个元素
* 3.remove():删除当前指向的元素
* 三个方法的使用步骤,按照1,2,3顺序调用
* 注意:迭代器一旦创建,集合中就不能添加和删除元素(长度不能更改了)
* 如果添加或者删除了元素,那么迭代器必须重新生成)
* 增强for循环 foreach,写起来更简单
* 功能不太全,删除的时候还是用iterator来删除
*/
public class Collection_2_Iterator {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add(1);
c1.add("asd");
//判断是否包含1
System.out.println(c1.contains(1));//true
//创建迭代器
Iterator it = c1.iterator();
//遍历元素,判断下面是否有元素
while (it.hasNext()) {
//获取并指向下一个元素
Object obj = it.next();
System.out.println(obj);//1 asd
}
// 使用完之后,想再次使用,需要重新创建迭代器
it = c1.iterator();
// 迭代器创建之后,不能添加和删除 , 必须重新生成迭代器
c1.add(3);
c1.add(4);
c1.remove(1);
it = c1.iterator();
while (it.hasNext()){
// 获取并指向下一个元素
Object obj = it.next();
System.out.println(obj);//asd 3 4
// 使用迭代器的时候,不能更改集合个数,所以删除数据的时候不能使用集合的删除,应该使用迭代器的删除
// c1.remove(obj);
it.remove();
}
System.out.println(c1.size()+ "======");//0======
}
}
10.contains方法解析
语法格式:boolean contains(Object o)
判断集合中是否包含某个对象o,如果包含返回true,如果不包含返回false。
contain方法是用来判断集合中是否包含某个元素的方法。
那么他的底层是怎么判断集合中是否包含某个元素的呢?
调用了equals方法进行比对
equals方法返回true,就表示
所以存放在一个集合中的类型,一定要重写equals方法
package com.company.Collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionText2 {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();
//向集合中存储对象
String s1 = new String("abc");
c.add(s1);
String s2 = new String("abc");
c.add(s2);
//集合中元素的个数
System.out.println("元素的个数是:" + c.size());
//新建的对象String
String x = new String("abc");
//c集合中是否包含x ? 结果猜测一下是true还是false ?
System.out.println(c.contains(x));//判断集合中是否存在“abc” true
}
}
11.remove方法解析
在上述代码基础上加入以下该行:
c.remove(u2);
System.out.println(c.size());//输出结果为:0
Collection cc = new ArrayList();
String s1 = new String("hello");
cc.add(s1);
String s2 = new String("hello");
cc.remove(s2);
System.out.println(cc.size());//输出结果为:0
去掉User类中的equals方法后,结果为:
由此可知remove中也调用了equals方法
重点:当集合的结构发生改变的时候,迭代器必须重新获取
当集合的结构发生改变的时候,迭代器必须重新获取,如果还是用以前老的迭代器,会出现异常。
Collection c2 = new HashSet();
Iterator it2 = c2.iterator();
c2.add(1);
c2.add(2);
c2.add(3);
c2.add(4);
c2.add(1);
c2.add(2);
此时便会出现异常:ConcurrentModificationException
package com.company.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionText4 {
public static void main(String[] args) {
Collection c2 = new ArrayList();
c2.add("abc");
c2.add("def");
c2.add("xyz");
Iterator it2 = c2.iterator();
while (it2.hasNext()){
Object o = it2.next();
//删除元素
//删除元素之后,集合的结构发生了变化,应该重新去获取迭代器
//但是,循环下一次的时候并没有重新获取迭代器,所以会出现异常:ConcurrentModificationException
//出异常根本原因:集合中元素删除了,但是没有更新迭代器
c2.remove(o);//直接通过集合删除元素,没有通知迭代器。(导致迭代器的快照和原集合状态不同)
System.out.println(o);
}
}
}
import java.util.ArrayList;
import java.util.List;
//List的特点:
// 有序可重复
// 有序:添加顺序和取出顺序一致
// 可重复:就是有可重复的数据
//ArrayList: 底层是个object数组,查改效率高,增删效率低
//LinkedList:底层是一个双向链表,增删效率高,查改效率低
public class Collection_3_List {
public static void main(String[] args) {
// 创建对象
List list = new ArrayList();
list.add(100);
list.add(123);
//[100, 123]覆写了ToString方法
System.out.println(list);
}
}
在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素:c.remove(o);
迭代过程中不能这样,会出现:ConcurrentModificationException
注意:
在迭代集合元素的过程当中,一定要使用迭代器Iterator的remove方法,删除元素,不要使用集合自带的remove方法删除元素。
可以使用Iterator的remove方法:
it2.remove();//删除的一定是迭代器指向的当前元素。
12.list接口的特点
list集合存储元素的特点:有序可重复
- 有序:List集合中的元素有下标。从0开始,以1递增
- 可重复:存储1,还可以再存储1
13.List接口的常用方法
1、在集合末尾添加元素:void add(Object element)
2、在指定下标添加元素:void add(int index , Object element)
这个方法使用不多,因为对于ArrayList集合来说效率较低。
3、根据下标获取元素:Object get(int index)
因为有下标,所以List集合有自己比较特殊的遍历方式。
通过下标遍历
for (int i = 0; i < List.size() ; i++){
Object o = List.get(i);
System.out.println(o);
}
4、获取指定对象第一次出现处的索引(下标):int indexof(Object o)
5、获取指定对象最后一次出现处的索引(下标):int lastIndexOf(Object o)
6、删除指定下标位置的元素:Object remove(int index)
7、修改指定下标元素:Object set(int index,Object element)
三、ArrayList集合
ArrayList集合底层采用了数组这种数据结构,底层是Object类型的数组Object []
ArrayList集合是非线程安全的。
数组的优点:检索效率比较高。
数组的缺点:随机增删元素的效率比较低。(向数组末尾添加、删除元素、效率还是很高的)、数组无法存储大数据量(无法找到一块非常巨大的连续的内存空间。)
但是ArrayList集合是往数组末尾添加元素,效率不受影响。
List list1 = new ArrayList();//默认初始化容量为10
List list2 = new ArrayList(20);//指定初始化容量
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//ArrayList底层是数组,下标从0开始
//默认初始化容量是10 扩大是初始化的1.5倍
//并且默认容量是第一次添加数据的时候设置的
//也就是说 我们 new ArrayList() 的时候,数组长度是为0的
public class Collection_4_List {
public static void main(String[] args) {
// 创建对象
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
// add(E e ) : 尾部添加
// add(int index , E e ) : 添加到指定位置
// set(int index, E e ) : 更改指定位置上的元素值
// remove(Object object) : 根据元素内容删除
// remove(int index) : 根据索引删除
// get(int index) : 获取指定位置的数据
list.add(1,4);
list.set(3,44);
System.out.println(list.get(2));
list.remove(1);//根据索引删除
list.remove(new Integer(1));//删除元素值为1
System.out.println(list);
// 传统遍历
for (int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
// foreach遍历
for (Object obj:list){
System.out.println(obj);
}
// 迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
1、ArrayList集合初始化容量
ArrayList集合默认初始化容量为:10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量为10)
2、ArrayList集合扩容
源代码的grow方法中使用了位运算:int newCapacity = oldCapacity + (oldCapacity >> 1);
10的二进制位:00001010
10的二进制右移1位是:00000101【5】
结论:一次扩容到原来的1.5倍
建议给定一个预估计的初始化容量,减少数组的扩容次数,这是ArrayList集合比较重要的优化策略。
3、ArrayList集合的构造方法
第一种方法
List list1 = new ArrayList();
List list2 = new ArrayList(100);
第二种方法:
- 1、先创建一个HashSet集合
- 2、将HashSet集合转换为ArrayList集合:
List list3 = new ArrayList(c);
Collection c = new HashSet();
c.add(100);
c.add(200);
c.add(300);
c.add(400);
List list3 = new ArrayList(c);
ArrayList排序
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
//ArrayList排序
public class Collection_5_SortList {
public static void main(String[] args) {
// 创建对象
List list = new ArrayList();
list.add(4);
list.add(2);
list.add(15);
list.add(3);
list.add(8);
Collections.sort(list);//调用排序方法
System.out.println(list);//[2, 3, 4, 8, 15]
}
}
四、LinkedList集合
LinkedList集合没有初始化容量,且不需要扩容
ListedList底层是一个双向链表:
1、链表的优点
由于链表上的元素在空间存储上内存地址不连续。
所以随机增删元素的时候不会有大量元素位移,因此随机增删元素的效率较高。(因为增删元素不涉及到大量元素位移)
在以后的开发中,如果遇到随机增删集合中的元素的业务比较多时,建议使用LinkedList。
2、链表的缺点
不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头结点开始遍历,直到找到为止。所以LinkList集合检索/查找的效率较低。
查阅效率较低,每一次查找某个元素的时候都需要从头结点开始往下遍历。
JDK5.0之后推出了一个新特性:叫做增强for循环,或者叫做foreach
1、普通遍历数组方法(普通for循环):
for(int i = 0 ; i < arr.length ; i++){
System.out.println(arr[i]);
}
2、增强for(foreach)方法
语法:for(元素类型 变量名:数组或集合) {System.out.println(变量名) }
foreach有一个缺点:没有下标
在需要使用下标的循环中,不建议使用增强for循环。
for(int data : arr){
//data就是数组中的元素(数组中的每一个元素)
System.out.println(data);
}
3、集合使用foreach方法
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class forEachText {
public static void main(String[] args) {
List<String> l = new ArrayList<>();
l.add("hello");
l.add("world!");
l.add("kitty");
//用迭代器方式遍历
Iterator<String> it = l.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
//使用下标方式
for ( int i = 0 ;i < l.size(); i++){
System.out.println(l.get(i));
}
//使用foreach
for (String s : l){ //因为泛型使用的是String类型,所以是String s
System.out.println(s);
}
}
}