前言
这里每种集合遍历方式和方法都有重复,所以只会在Collection集合中讲遍历方法和方法,如果集合的遍历方法和方法有独特的会单独说,其它重复的只在Collection集合讲
Collection集合
所谓的单列集合就是集合中的一个元素,只存储一个值,而Collection集合就是所有单例集合的祖宗,其所有的方法,在Collection的子孙类中均可使用
- Collection集合的常用方法
方法 | 作用 |
---|---|
add(E e) | 添加元素,添加成功返回true |
clear() | 清空集合中的元素 |
isEmpty() | 判断集合是否为空,为空就返回true,反之返回false |
sizse() | 获取集合大小 |
contains(Object obj) | 判断集合中是否包含某个元素 |
remove(E e) | 删除某个元素,如果有多个重复的元素只删除第一个 |
toArray() | 把集合转换成数组,数组是Object类型 |
public class Demo01 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
// add(E e) 添加元素,添加成功返回true
list.add("乾巧");
// clear() 清空集合元素
list.clear();
System.out.println(list);
// isEmpty() 判断集合是否为空,为空就返回true,反之返回false
System.out.println(list.isEmpty());
// size() 获取集合长度
int size = list.size();
// contains(Object obj) 判断集合中是否包含某个元素
list.add("草加");
System.out.println(list.contains("草加"));
// remove(E e) 删除某个元素,如果有多个重复的元素只删除第一个
list.remove("草加");
// toArray() 把集合转换成数组,数组是Object类型
Object[] array = list.toArray();
}
}
- Collection的遍历方式
迭代器
- 创建一个迭代器对象:
Iterator<String> iterator = list.iterator();
- 使用while循环判断当前位置是否有元素
- 使用
iterator.next()
取出当前位置的元素,并把指针指向下一个元素位置 - 注意遍历时不要直接输出多个
iterator.next()
,因为这样可能会导致遍历的越界错误 - 有并发修改异常的问题
public class Demo02 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("乾巧");
list.add("草加");
list.add("五代");
list.add("一条");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
}
}
增强for循环
- 增强for循环本质上是迭代器的简化写法,可以用来遍历集合和数组
- 有并发修改异常的问题
public class Demo02 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("乾巧");
list.add("草加");
list.add("五代");
list.add("一条");
for (String s : list) {
System.out.println(s);
}
}
}
forEach(Lambda)
- 集合.forEach(集合元素 -> {代码})
- 底层也是迭代器,也会有并发修改异常的问题
public class Demo02 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("乾巧");
list.add("草加");
list.add("五代");
list.add("一条");
list.forEach(item -> System.out.println(item));
}
}
List集合
List集合是Collection集合的子接口,它底下有两个常用的实现类ArrayList和LInkedList,他们里面的元素有素
有序、可重复、有索引
- List集合独特的方法
方法 | 作用 |
---|---|
add(int index, E element) | 在某个索引位置插入一个元素 |
remove(int index) | 根据索引删除元素,返回被删除元素 |
get(int index) | 返回集合中指定位置的元素 |
set(int index, E element) | 修改指定索引位置的元素,修改成功后,返回原来的数据 |
public class Demo02 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("刘备");
list.add("张飞");
list.add("关羽");
list.add("赵云");
list.add("马超");
// add(int index, E element) 在某个索引位置插入一个元素
list.add(0,"曹操");
// remove(int index) 根据索引删除元素,返回被删除元素
list.remove(0);
// get(int index) 返回集合中指定位置的元素
String s = list.get(3);
// set(int index, E element) 修改指定索引位置的元素,修改成功后,返回原来数据
list.set(1,"曹植");
}
}
- List集合独特的遍历方式
for循环
public class Demo02 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("刘备");
list.add("张飞");
list.add("关羽");
list.add("赵云");
list.add("马超");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
ArrayList集合
ArrayList集合的底层是通过数组存储数据的。它利用无参的构造器创建一个ArrayList集合,然后在底层创建一个默认长度为0的数组,在添加第一个元素时,底层会创建一个新的长度为10的数组,用来替换旧的数组,当这个集合存满10个时,底层的数组会扩容1.5倍,如果一次添加多个元素的时候,扩容1.5倍还放不下,则新建数组的长度以实际为准
- ArrayList的优缺点
- 通过索引查询数据快。查询数据时通过地址值和索引进行定位,这是因为底层是数组的原因
- 删除效率低。这是因为底层是数组,删除数据时可能要把后面的元素往前面移动,这样会影响效率
- 添加效率极低。在容量足够的情况下,可能要把后面的数据往后移动;容量不够的情况下,要对数组进行扩容,即重新定义一个数组,再把数据一个个添加进去
- ArrayList的应用场景
- 当需要通过索引来查询数据的时候,比如根据随机索引查找数据就会非常高效,或者数据量不是很大的时候,查询也快
- 当数据量很大,然后又需要进行频繁的增删操作就不适合用ArrayList来存储数据
LInkedList集合
LInkedList集合底层是基于双链表来实现 ,除了头尾结点,其它阶段都存储着前后结点的地址值,所以通过LInkedList集合查找数据的速度较快,但相对于ArrayList查找数据的速度还是很慢
- 优缺点
- 增加和删除数据很快,因为LInkedList是基于双向链表实现,所以在增加或删除元素的时候只需要改变元素的前后结点存储的地址即可
- LInkedList查询元素较慢,但是头结点的查询非常快
方法 | 作用 |
---|---|
addFirst(E e) | 在列表的头部插入指定元素 |
addLast(E e) | 将指定元素插入列表的末尾 |
getFirst() | 返回列表的第一个元素 |
getLast() | 返回列表的最后一个元素 |
removeFirst() | 删除列表第一个元素 |
removeLast() | 删除列表最后一个元素 |
public class Demo03 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
// addFirst(E e) 在列表的头部插入指定元素
list.addFirst("广州");
list.addFirst("深圳");
System.out.println(list);
System.out.println("--------------------");
// addLast(E e) 将指定元素插入列表的末尾
list.addLast("杭州");
list.addLast("上海");
System.out.println(list);
System.out.println("--------------------");
// getFirst() 返回列表的第一个元素
String first = list.getFirst();
System.out.println(first);
System.out.println("--------------------");
// getLast() 返回列表的最后一个元素
String last = list.getLast();
System.out.println(last);
System.out.println("--------------------");
// removeFirst() 删除列表第一个元素
list.removeFirst();
System.out.println(list);
System.out.println("--------------------");
// removeLast() 删除列表最后一个元素
list.removeLast();
System.out.println(list);
}
}
- LInkedList的应用场景
- 队列,先进先出,要频繁的对队首和队尾进行操作
- 栈,先进后出
Set集合
Set集合是Collection集合的子接口,它有两个实现类,分别是HashSet和TreeSet,Set集合里面的元素
无序、不可重复、无索引
- 无特殊方法
- 无特殊遍历方式
哈希值
- Java中每一个对象都有一个哈希值,是一个int类型是数据
- 每一个对象都可以通过Object类提供的hashCode()方法获取对象的哈希值
- 一般情况下,每一个对象的哈希值都不同,但因为哈希值是一个int类型的数据,取值范围有限,也有可能出现两个对象的哈希值相同,这种情况叫做哈希碰撞
HashSet集合
HashSet集合里面的元素是
不重复、无序、无索引
的,底层是基于哈希表实现的,在JDK8之前哈希表是数组+链表
,JDK8开始哈希表是数组+链表+红黑树
- HashSet集合对相同内容的对象去重机制
- 在HashSet集合中认为只要两个对象的哈希值不同,那么就是两个不同的对象,但当我们创建两个内容一样的对象时,用HashSet就无法达到去重的效果
- 去重相同内容的对象,我们必须要在对象的类中重写
equals
和hashCode
方法即可完成去重
- 哈希表集合添加元素的原理
- 先通过对象的哈希值计算出对象在数组中的位置
- 如果该位置没有元素,就直接添加进去
- 如果这个位置有元素,就是用
equals()
比较,相同就不添加,不相同就要分为两种情况。第一种是JDK8之前是新元素取代旧元素的位置,成为表头结点,而旧元素挂在新元素下面;第二种是JDK8开始,新元素就直接挂在链表的最后即可
LinkedHashSet
LInkedHashSet集合中的元素是
有序、不重复、无索引
的,它的底层是基于数组、单链表、红黑树、双链表的哈希表
TreeSet集合
TreeSet集合里的元素是
不重复、无索引、可排序(默认是升序)
的,底层原理是基于红黑树实现
- 排序
- 当传入的集合元素类型是基本数据类型的时候,就直接按照基本数据类型的值排序,字符串类型就按照首字母或其后面的字母顺序排序即可
- 当传入的元素是引用数据类型的时候,我们就要自定义排序的规则,有以下两种方式
方式一:让类实现Compareble接口,重写里面的comparaTo方法制定规则
public class Demo04 {
public static void main(String[] args) {
TreeSet<Student> list = new TreeSet<>();
list.add(new Student("张三",19));
list.add(new Student("李四",16));
list.add(new Student("王五",20));
list.add(new Student("赵六",18));
list.forEach(item -> System.out.println(item));
}
}
class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public int compareTo(Student o) {
return this.getAge() - o.getAge();
}
}
方式二:通过调用TreeSet集合的有参构造器,可以设置Comparator对象(比较器对象,用于制定比较规则)
public class Demo04 {
public static void main(String[] args) {
TreeSet<Student> list = new TreeSet<>((o1, o2) -> o1.getAge() - o2.getAge());
list.add(new Student("张三",19));
list.add(new Student("李四",16));
list.add(new Student("王五",20));
list.add(new Student("赵六",18));
list.forEach(item -> System.out.println(item));
}
}
class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}