集合
由于不同的数据结构(数据的组织,存储方式),所以Java为我们提供了不同的集合,
但是不同的集合他们的功能都是相似,不断的向上提取,将共性抽取出来,这就是集合体系结构形成的原因
体系结构:
怎么学习?最顶层开始学习,因为最顶层包含了所有的共性
怎么使用?使用最底层,因为最底层就是具体的实现
Collection集合
1.Collection是一个接口,它是List和Set的父接口
。因为是接口所以不能直接创建Collection对象,只能创建它的子类对象的方式来调用它的方法。
2.Collection的成员方法:
boolean add(E e) //添加元素
void clear() //清空集合
boolean contains(Object o)//集合是否包含某个元素
boolean isEmpty()//集合是否为空
boolean remove(Object o) //删除元素
int size() //返回集合的大小
Object[] toArray() //把集合转化成Object[]
Iterator<E> iterator() //返回迭代器
3.Collection集合的遍历方式
①.toArray(),可以把集合转换成数组,然后遍历数组即可
②.iterator(),可以返回一个迭代器对象,我们可以通过迭代器对象来迭代集合
注意:这里说的是集合的遍历方式,很多同学会下意思的觉得不是还可以使用for循环和索引来遍历。但是那个其实是遍历ArrayList的遍历方式。我们说ArrayList属于Collection集合的一种。其他有些Collection集合并不能使用这种方式遍历。所以这里Collection集合的遍历方式指的是所有Collection集合通用的方式
使用toArray()来遍历集合:
//创建集合对象
Collection c = new ArrayList();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
//获取数组
Object[] objs = c.toArray();
//遍历数组
for (int i = 0; i < objs.length; i++) {
System.out.println(objs);
}
使用iterator()方法来遍历集合 (关于迭代器的详细介绍请看下一个知识点)
Collection c = new ArrayList();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
//获取迭代器对象
Iterator it = c.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
Iterator迭代器
1.Iterator是一个接口叫迭代器,可以用来遍历集合。
2.Iterator的常用方法
boolean hasNext() //判断是否有元素可以获取
E next() :返回下一个元素
3.使用Iterator遍历集合
示例代码:
//因为Collection是一个接口,不能创建实例所 //以只能创建子类对象。
Collection c = new ArrayList();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
//获取迭代器对象
Iterator it = c.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
4.使用过程中的常见问题
①问题描述:使用next方法获取下一个元素,如果没有元素可以获取,则出现NoSuchElementException
解决方法:所以为了避免出现这个错误应该在每次调用next方法之前先调用hasNext方法去查看是否还有元素。如果没有就不调用next方法。如果有就调用,所以就有了我们遍历Iterator的代码;
②并发修改异常ConcurrentModificationException
问题描述:当我们一边使用迭代器遍历一边使用集合的方法删除集合的元素或者往集合里添加元素就会出现并发修改异常
迭代器是依赖于集合的,相当于集合的一个副本,当迭代器在操作的时候它会去检查迭代器的元素数量和集合里面的元素的数量进行比较如果发现和集合不一样,则抛出异常
解决方法:
方案1.别使用迭代器
方案2.在使用迭代器进行遍历的时候使用迭代器来进行修改。
方案2解决后的代码:
List c = new ArrayList();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
ListIterator lit = c.listIterator();
while(lit.hasNext()) {
String s = (String)lit.next();
if(s.equals("java")) {
lit.add("android");
}
}
注意:Iterator没有add和remove方法。但是他的子类ListIterator有。但是我们的Collection接口
的iterator方法并不能返回ListIterator。只有List接口的listIterator方法可以返回ListIterator。
List集合
1.List的特点
有序的(存储和读取的顺序是一致的)
有整数索引
允许重复的
2. List的特有功能
void add(int index, E element)
E get(int index)
E remove(int index)
E set(int index, E element)
3.常见子类
ArrayList
底层是数组结构,查询快,增删慢
LinkedList
底层结构是链表,查询慢,增删快
4.如何选择使用不同的集合?
如果查询多,增删少,则使用ArrayList
如果查询少,增删多,则使用LinkedList
如果你不知道使用什么,则使用ArrayList
set集合
1.Set集合的特点:
无序(存储和读取的顺序有可能不一样)
不允许重复(要求元素唯一)
没有索引
2.Set集合的遍历方式
①转数组遍历
private static void method(Set<String> set) {
//转数组
Object[] objs = set.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs);
}
}
②使用迭代器遍历
private static void method2(Set<String> set) {
//迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
③使用增强for遍历
Set<String> set = new HashSet<String>();//父接口引用指向子类对象
//添加元素对象
set.add("hello");
//set.add("world");
System.out.println(set.add("java"));
System.out.println(set.add("java"));
for(String s : set) {
System.out.println(s);
}
HashSet集合
1.HashSet实现了Set接口所以也有Set集合的特点。Set有一个特点是不允许重复。所以HashSet肯定也是不允许有重复元素的。
2.HashSet保证元素唯一的原理
HashSet因为元素不能重复,所以他会在我们添加元素的时候,也就是调用add方法的时候去判断我们想要使用add方法添加到集合的元素和HashSet已有的方法进行判断是否有重复的,如果重复了就不会把这个东西添加到集合。如果没有重复的话就把这个元素添加到集合。
那么HashSet是怎么去判断元素是否重复呢?
通过查看源码发现:
HashSet的add()方法,首先会使用当前集合中的每一个元素和新添加的元素进行hash值比较,
如果hash值不一样,则直接添加新的元素
如果hash值一样,比较地址值或者使用equals方法进行比较
比较结果一样,则认为是重复不添加
所有的比较结果都不一样则添加
hash值:hash值其实是地址值。但是其实这是在没有重写hashcode方法的情况下。因为我们所有类的根类是Object。Object里的hashcode方法返回的hash值就是地址值。但是如果我们把一个类的hashcode方法给重写了,这个时候这个类的对象的hash值就不表示地址值了。
如果一个类没有实现equals方法的话。默认就会调用Object的equals方法。而Object的equals方法其实就是用==判断。而==判断基本数据类型是判断值,而判断引用数据类型则是判断地址值。
思考:如果我用HashSet直接去存储如下的Student类的对象的话,集合里会有几个元素?其实在我们的印象里s2和s3应该是重复的元素。但是他们为什么都被添加到了集合里?我们应该怎么解决。(想想HashSet判断元素重复是怎么判断的)
答案:会全部都添加进去,因为HashSet集合先比较hash值是否相同。如果不同,就认为不是重复的。因为Student没有重写HashCode方法所以hash值就是对象的地址值。而s2和s3都是new出来的,地址值(hash值)肯定不同。这个时候就会直接认为两个元素没有重复。
public class HashSetDemo2 {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<Student>();
Student s = new Student("zhangsan",18);
Student s2 = new Student("lisi",19);
Student s3 = new Student("lisi",19);
hs.add(s);
hs.add(s2);
hs.add(s3);
}
}
class Student {
String name;
int age;
public Student(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
解决方案:(提示,重写代码可以直接快捷键生成)
重写Student的hashcode和equals方法。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
|
|