集合
数组长度固定,集合长度可变
若对象数据比较多,而且数据个数不固定时,使用集合存储。
一、Collection集合
是一个接口 ,保存单个数据
有两个主要子接口List和Set。
List 有ArrayList、LinkedList、Vector 三个主要实现类
Set有HashSet、LinkedHashSet 两个主要实现类
1.1Collection通用的方法
Collection有通用的方法,List和Set都可以使用。
public class Demo {
public static void main(String[] args) {
//使用多态创建多个Collection类型的子类
//c1~c6 只能调用Collection接口里的方法
Collection c1 = new ArrayList();//Collection是接口不能直接new对象
Collection c2 = new LinkedList();
c2.add("good");
c2.add(13);
c2.add("张三");
Collection c3 = new Vector();
Collection c4 = new HashSet();
Collection c5 = new LinkedHashSet();
Collection c6 = new TreeSet();
c1.add(1);//添加元素
c1.add(2);
c1.add("hello");
c1.remove(1);//删除元素
c1.addAll(c2);//将一个Collection添加到另一个Collection里
c1.removeAll(c2);//将一个Collection里的另一个Collection移除
c1.clear();//清空
c1.contains("hallo");//判断c1中有没有"hello"元素,返回结果为boolean类型
c1.containsAll(c2);//判断c1中是否包含c2
c1.isEmpty();//判断c1是不是空的
c1.size();// 元素的个数
Object[] objects = c1.toArray();//将c1转换为数组
System.out.println(c1.toString());
}
}
1.2迭代器
Iterator 接口用来遍历Collection。
package com.atguigu.collection;
import java.util.ArrayList;
import java.util.Collection;
public class IterDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("张三");
c.add("李四");
c.add("jack");
c.add("rose");
c.add("王五");
c.add("tony");
c.add("tom");
c.add("jerry");
}
}
1.2.1 遍历Collection的几种方式
1.通过获取迭代器iterator,调用hasNext()判断是否有下一个元素,使用next()获取元素。while循环
Iterator iterator = c.iterator();//获取到Collection对象的迭代器
while (iterator.hasNext()){
System.out.println(iterator.next());
}
2.增强for循环
for (Object o : c) {
System.out.println(o);
}
1.3泛型
虽然Collection可以存储不同类型,但通常情况下也是存储同一类型。业务逻辑和从数据处理角度分析,方便操作数据。
使用泛型——>在定义Collection的时候,就规定元素的数据类型。将运行时的错误提前,在编译的时候暴露出来。
<> 泛型中规定的是一个对象,不能写基本数据类型,可以写基本包装类型Integer等。创建对象时泛型可以省略,但<>建议保留。泛型不支持多态。
public static void main(String[] args) {
Collection<Integer> scores = new ArrayList<>();
//Collection<Object> scores = new ArrayList<Integer>(); 泛型不支持多态
scores.add(98);
//scores.add("87");编译报错
int sum = 0 ;
//使用时,类型不再是Object类型,可以直接使用泛型声明的类型
for (Integer score : scores) {
sum+=score;
}
}
可以在类中自定义泛型,然后在别的类中调用
package com.atguigu.collection;
//<T>声明泛型,在整个类中都可以使用,可以作为参数也可以作为返回值
public class MyGeneric<T> {
public void test(T t) {
System.out.println(t);
}
public T demo() {
return null;
}
//在泛型中还可以声明别的泛型<E>与<T>无关
public <E> void foo(E e) {
System.out.println(e);
}
//在泛型中还可以声明别的泛型<G> ,返回G类型数据
public <G> G bar() {
return null;
}
}
package com.atguigu.collection;
public class MyGenericDemo {
public static void main(String[] args) {
MyGeneric<String> mg = new MyGeneric<>();
//mg.test(2);报错,只能传入String类型的参数
mg.test("hello");
mg.demo();
MyGeneric<Integer> mg1 = new MyGeneric<>();
mg1.test(34);
mg1.foo('a');//此时为Character类型
}
}
泛型的通配符:? 使用时不能传入?因为无法存放数据。
通常?不会单独使用,会配合super以及extends规定泛型的上下限。
Collection<? extends Person>
表示需要的对象为Person类,或者它的子类
Collection<? super Person>
表示需要的对象是Person类,或者它的父类。
泛型通配符的使用场景:
在java.util包里还有一个Collections 工具类 通常用来快速操作LIst 的
<T extends Comparable<? super T>> void sort(List list)
将 Teacher 类型赋值给泛型,T还不能直接将 Teacher 赋值给 T,因为方法声明里要求 T 类型要实现 Comparable接口,并实现 CompareTo方法。Comparable接口也允许泛型,泛型的类型必须要是 Teacher类或者它的父类类型。
1.4 List接口
List接口继承自Collection接口, 所有Collection接口里的方法List创建出来的实例对象都可以调用,常见方法如add/ addAll/ remove/ removeAll/ clear/ isEmpty/ size。
Collection接口里没有get方法,不能获取到元素,因为Collection有两个子接口,List(有序)和Set(无序),所以不能通过序号编号等获取到指定元素。但是List里元素有序,可以通过序号获取到指定位置的元素,也可以调用set方法。
除了Collection中的两种遍历方法,List中还有一种for循环的遍历方式
package com.atguigu.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo {
public static void main(String[] args) {
List<String> l = new ArrayList<>();
l.add("张三");
l.add("李四");
l.add("王五");
l.add("赵六");
l.add("托尼");
l.add("汤姆");
System.out.println(l.get(0));
l.set(3,"小强");//将下标为3的元素替换为小强
System.out.println(l);
/* 遍历方法
1.使用迭代器
Iterator<String> iterator = l.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
2.增强for循环
for (String s : l) {
System.out.println(s);
}
*/
//List多一种遍历的方式,就是fori循环,通过下标来操作元素
for (int i = 0; i < l.size(); i++) {
System.out.println(l.get(i));
}
}
}
在List接口中,删除元素时要注意。若有相同的元素紧挨着就会出现删不干净的情况,需要倒着遍历删除!!
**注意:增强for循环不能删除和添加元素(不能修改数据的长度。**原因是因为在List的add和remove方法被调用以后,modCount变量都会自增。在迭代器的next方法里,会判断modCount和expectedModCount的值是否相等,如果不相等就会报错!
package com.atguigu.list;
import java.util.ArrayList;
public class ListDemo2 {
//删除元素的注意事项
public static void main(String[] args) {
ArrayList<String> heroes = new ArrayList<>();
heroes.add("钢铁侠");
heroes.add("蜘蛛侠");
heroes.add("雷神");
heroes.add("蝙蝠侠");
heroes.add("超人");
heroes.add("闪电侠");
heroes.add("雷神");
heroes.add("雷神");
heroes.add("海王");
/* for (int i = 0; i < heroes.size(); i++) {
if (heroes.get(i).equals("雷神")){
//heroes.remove(i);
heroes.remove("雷神");
i--;//再重新看一次删除的那个位置的值,不然靠在一起的同样元素删不掉
}
}*/
//删除满足条件的所有元素,需要倒着遍历
for (int i = heroes.size() - 1; i >= 0; i--) {
if (heroes.get(i).equals("雷神")) {
heroes.remove(i);
}
}
System.out.println(heroes);
/*增强for循环不能删除和添加元素(不能修改数据的长度)
原因是因为在List的add和remove方法被调用以后,modCount变量都会自增
在迭代器的next方法里,会判断modCount和expectedModCount的值是否相等,如果不相等就会报错!
*/
}
}
1.4.1 List的常用子类
ArrayList 实现了List接口,内部维护了一个数组,关于下标的方法都可以使用,例如 add、remove、set、indexOf等。
ArrayList 和LinkedList的区别:
最大的区别就在于数据结构不同:
1.ArrayList 内部维护了一个数组,查询速度较快,但是插入数据速度较慢。
2.LinkedList 内部是一个链表的数据结构,查询速度较慢,插入速度较快。
一、ArrayList源代码分析:
1.创建ArrayList的时候,会在内部维护一个空的数组。
2.当调用add方法的时候,才会给数组指定长度(懒加载)。
3.JDK7(包括)以后,都是懒加载,只要不添加元素,内部维护的就是一个长度为0 的空数组;
JDK6(包括)以前,上来就直接给内部的数组指定长度为10的空数组。
4.当数组的数据填满了以后,会扩展为原来数组长度的1.5倍。
当添加第11个元素的时候,minCapacity计算以后的结果为11
private void grow(int minCapacity) { // minCapacity --> 11
int oldCapacity = elementData.length; // oldCapacity --> 10
int newCapacity = oldCapacity + (oldCapacity >> 1); // newCapacity = 10 + (10 >> 1) ==> 15
if (newCapacity - minCapacity < 0) // 15 - 11 < 0
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity); // 把elementData原有的数据拷贝到 elementData,并将长度修改为15
}
二、LinkedList源代码分析:
内部是维护了一个双向链表,Node为节点,Frist是链表第一个,Last为最后一个。一个Node中至少有三个数据,添加元素时不是直接放到链表中,而是封装一下变成Node对象,Node对象中保存三个数据,第一个为prev(上一个数据),第二个是添加的元素数据,第三个为next(下一个数据)。添加第一个数据时,First和Last都为空null,走完之后新建一个节点 在Last后面,成为新的最后一个。。。
1.5 Set接口
Set接口没有在Collection父接口的基础上新增方法。
Set的特点:
-
Set里存储的数据是无序的
-
Set里存储的数据都不允许重复。
2.1 如何保证HashSet里的元素不重复。
1)通过hashCode()方法来判断元素的哈希值是否一样。
2)如果哈希值一样,再调用元素的equals 方法, 看equals的结果是否是true
3)如果哈希值一样,equals的结果也是true,就会认为这两个元素是同一个元素,只会存入一个数据。
TreeSet注意事项:
- 存入的元素必须要实现Comparable接口
- TreeSet 会根据Comparable 接口里的 compareTo 方法的返回值对元素进行排序
- 如果compareTo方法的返回值是0,会认为是同一个元素。