目录
集合的继承实现关系:
Collecton接口常用的子接口有:List接口、Set接口
List接口常用的子类有:ArrayList类、LinkedList类
Set接口常用的子类有:HashSet类、LinkedHashSet类
List允许有重复的元素,有序。
Set不予许有重复元素,无序。
Collection接口
集合中的顶层接口,它中定义的所有功能子类都可以使用,是共性的功能,是集合中所有实现类必须拥有的方法。
Collection接口的基本方法:
创建集合的格式:
方式1:Collection<元素类型> 变量名 = new ArrayList<元素类型>();
方式2:Collection 变量名 = new ArrayList();
方式1创建的集合,只能存储<>中指定的元素类型,该方式为常用方式
方式2创建的集合,集合的元素类型默认为Object类型,即任何类型的元素都可以存储。
Iterator迭代器
Collection集合元素的通用获取方式:
在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。
这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。
所以可以看到Collection和Map都实现了Iterator接口。
Iterator接口的常用方法如下:
- hasNext()方法:用来判断集合中是否有下一个元素可以迭代。如果返回true,说明可以迭代。
- next()方法:用来返回迭代的下一个元素,并把指针向后移动一位。
在Collection接口描述了一个抽象方法iterator方法,所有Collection子类都实现了这个方法,并且有自己的迭代形式。
for each遍历(增强for循环)本质上也是迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
//对获取出的元素s,进行判断,是不是有"abc"
// 有就插入一个ABC
if(s.equals("abc")){
list.add("ABC");
}
System.out.println(s);
}
运行上述代码发生了错误 java.util.ConcurrentModificationException这是什么原因呢?
在迭代过程中,使用了集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性。
并发修改异常解决办法:在迭代时,不要使用集合的方法操作元素。
那么想要在迭代时对元素操作咋办?通过ListIterator迭代器操作元素是可以的,ListIterator的出现,解决了使用Iterator迭代过程中可能会发生的错误情况。但是仅限于删除remove
public void ttt() {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for (String item : list) {
if (item.equals("3")) {
System.out.println(item);
list.remove(item);
}
}
System.out.println(list.size());
}
// 遍历的时候移除使用Iterator里面的remove方法。
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
String data = (String) iterator.next();
if (data.equals("3")) {
// 不管在foreach还是Iterator遍历过程中都不允许使用list改变长度
// 如果是移除元素,可以使用Iterator里面的remove,但是对于添加
// Iterator就没有提供,可以参考ListIterator
iterator.remove();
System.out.println(data);
}
}
System.out.println(list.size());
// 不管在foreach还是Iterator遍历过程中都不允许使用list改变长度,如果是移除元素,可以使用Iterator里面的remove,但是对于添加Iterator就没有提供
List接口
特点(也是所有实现类的特点):
1、它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
2、它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
3、 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
List接口的常用子类有:
ArrayList集合
LinkedList集合
ArrayList集合
常用API
1、增加元素方法
add(Object e):向集合末尾处,添加指定的元素
add(int index, Object e):向集合指定索引处,添加指定的元素,原有元素依次后移
2、删除元素删除
remove(Object e):将指定元素对象,从集合中删除,返回值为被删除的元素
remove(int index):将指定索引处的元素,从集合中删除,返回值为被删除的元素
3、替换元素方法
set(int index, Object e):将指定索引处的元素,替换成指定的元素,返回值为替换前的元素
4、查询元素方法
get(int index):获取指定索引处的元素,并返回该元素
Vector:
Vector集合数据存储的结构是数组结构,为JDK中最早提供的集合。Vector中提供了一个独特的取出方式,就是枚举Enumeration,它其实就是早期的迭代器。此接口Enumeration的功能与 Iterator 接口的功能是类似的。Vector集合已被ArrayList替代。枚举Enumeration已被迭代器Iterator替代。
Vector & ArrayList比较
相同点:
Vector与ArrayList本质上都是一个Object[] 数组,都实现了List这个接口
不同点:
1)Vector是线程安全的集合类,ArrayList并不是线程安全的类。Vector类对集合的元素操作时都加了synchronized,保证线程安全。
2)Vector与ArrayList的扩容并不一样,Vector默认扩容是增长一倍的容量,Arraylist是增长50%的容量。ArrayList就有利于节约内存空间。
Vector无论查询还是增删都很慢,所以被ArrayList替代了。
LinkedList
LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法.
public void test() {
LinkedList<String> link = new LinkedList<String>();
//添加元素
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
//获取元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
//删除元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
while(!link.isEmpty()){ //判断集合是否为空
System.out.println(link.pop()); //弹出集合中的栈顶元素
}
}
Set
Set中元素无序不重复
HashSet:
无序:HashSet集合不能保证的迭代顺序与元素存储顺序相同。
不重复:HashSet集合,采用哈希表结构存储数据,保证元素唯一性的方式依赖于两个方法:hashCode()与equals()方法。
当向哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是Object类中的hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。即就是在给哈希表中存放对象时,会调用对象的hashCode方法,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为哈希冲突,这时会调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回的是true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。
hashCode()与 equals() 的相关规定:
1、如果两个对象相等,则 hashcode 一定也是相同的
2、两个对象相等,两个 equals() 方法返回 true
3、两个对象有相同的 hashcode 值,它们也不一定是相等的
因为HashSet默认是根据变量的地址判断是不是已经加入HashSet了。
这样的话:
Student stu1 = new Student(1, "张三", 20);
Student stu2 = new Student(1, "张三", 20);
stu1和stu2都能够加入HashSet里面,因为他们的地址不一样,但是这其实是同一个Student,只是分别通过不同的方式new出来,如果stu1和stu2都能够加入的话,那么这个学生就在HashSet里面重复了。
所有的类都继承自Object类,Object类里面有hashCode和equal这两个方法,
HashSet调用add方法时候判断这个对象有没有已经在Set里面就是根据equal方法
解决方法就是:我们自己来判断是不是已经存在,选择你要比较的属性,覆写hashCode和equal方法
LinkedHashSet:
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序?
在HashSet下面有一个子类LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。
public void test4() {
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
TreeSet:
Java中的TreeSet是Set的一个子类,TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一。
那TreeSet为什么能保证元素唯一,它是怎样排序的呢?
public void test5() {
TreeSet<Student> treeSet = new TreeSet<>();
treeSet.add(new Student(1, "张三", 23));
treeSet.add(new Student(2, "李四", 13));
treeSet.add(new Student(3, "周七", 13));
treeSet.add(new Student(4, "王五", 43));
treeSet.add(new Student(5, "赵六", 33));
System.out.println(treeSet);
}
执行结果:
出错,会抛出一个异常:java.lang.ClassCastException: com.situ.mall.test.Student cannot be cast to java.lang.Comparable
显然是出现了类型转换异常。原因在于我们需要告诉TreeSet如何来进行比较元素,如果不指定,就会抛出这个异常
所以我们需要指定比较规则:
需要在自定义类(Student)中实现```Comparable```接口,并重写接口中的compareTo方法
几种重写方法:
@Override
public int compareTo(Student o) {
return 0; //当compareTo方法返回0,元素值每次比较,都认为是相同的元素
return >0; //当compareTo方法返回值正数,元素值每次比较,都认为新插入的元素比上一个元素大,读取时就是正序排列的
return <0; //当compareTo方法返回值负数,元素值每次比较,都认为新插入的元素比上一个元素小,读取时就是倒序排列的
}
}
按照年龄来排序,年龄相同按照名字排序:
public int compareTo(Student o) {
int num = this.age - o.age; //年龄是比较的主要条件
return num == 0 ? this.name.compareTo(o.name) : num;//姓名是比较的次要条件
}
按照姓名长度排序:
public int compareTo(Student o) {
int length = this.name.length() - o.name.length(); //比较长度为主要条件
int num = length == 0 ? this.name.compareTo(o.name) : length; //比较内容为次要条件
return num == 0 ? this.age - o.age : num; //比较年龄为次要条件
}
Map接口:
1、Collection中的集合,元素孤立存在。向集合中存储元素采用一个个元素的方式存储。
2、Map中的集合,元素成对存在。每个元素由键与值两部分组成,通过键可以找对所对应的值。
3、Collection中的集合称为单列集合,Map中的集合称为双列集合。
4、需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
(Key可以为null但是只能有一个为null,值可以多个为null)
5、Map中常用的集合为HashMap集合、LinkedHashMap集合。
HashMap<K,V>:
存储数据采用的哈希表结构,元素的存取顺序不能保证一致。
由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。(存放自定义类的时候重写,API中的类不需要)
LinkedHashMap<K,V>:
HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;
通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
Map集合遍历键找值方式:
一、键找值方式:即通过元素中的键,获取键所对应的值。
1.获取Map集合中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。
2.遍历键的Set集合,得到每一个键
3.根据键,获取键所对应的值
二、Map集合遍历键值对方式
键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
1.获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。
2.遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象
3.通过键值对(Entry)对象,获取Entry对象中的键与值。
注意:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。