一. Collection 单列集合
1. Collection代表单列集合,每个元素(数据)只包含一个值
2. Collection集合特点
① List系列集合:添加的元素是有序、可重复、有索引。
ArrayList、LinekdList:有序、可重复,有索引
② Set系列集合:添加的元素是无序、不重复、无索引
HashSet:无序、不重复、无索引
LinkedHashSet:有序、不重复、无索引
TreeSet:按照大小默认升序排序、不重复、无索引
3. Collection 集合的常用方法
Collection 是所有单列集合的父类,他规定的方法是全部单列集合都会继承(包括List<E>集合 和 Set<E> 集合)
常用方法 | 说明 |
boolean add(E e) | 添加元素,成功返回true |
addAll() | 把集合的全部元素移动到另一个集合中 |
void clear() | 清空集合中的元素 |
boolean isEmpty() | 判断集合是否为空 是则返回true |
int size() | 获取集合的大小 |
boolean contains(Object o) | 判断集合是否包含某个元素 |
boolean remove(Object o) | 删除某个元素,如果有多个默认删除第一个 |
Object[] toArray() | 将集合转换成数组 |
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
//boolean add(E e) 添加元素,成功返回true 因为可重复 所以一定会返回true
c.add("卡莎");
c.add("泰坦");
c.add("洛");
System.out.println(c);//[卡莎, 泰坦, 洛]
//void clear() 清空集合中的元素
c.clear();
System.out.println(c);//[卡莎, 泰坦, 洛]
//boolean isEmpty() 判断集合是否为空 是则返回true
System.out.println(c.isEmpty());//true
c.add("卡莎");
c.add("泰坦");
c.add("洛");
c.add("A");
c.add("洛");
System.out.println(c.isEmpty());//false
//int size() 获取集合的大小
System.out.println(c.size());//5
//boolean contains(Object o) 判断集合是否包含某个元素
System.out.println(c);//[卡莎, 泰坦, 洛, A, 洛]
System.out.println(c.contains("a"));//false
System.out.println(c.contains("A"));//true
//boolean remove(Object o) 删除某个元素,如果有多个默认删除第一个
c.remove("洛");
System.out.println(c);//[卡莎, 泰坦, A, 洛]
//Object[] toArray() 将集合转换成数组
Object[] object = c.toArray();
System.out.println(Arrays.toString(object));//[卡莎, 泰坦, A, 洛]
String[] strings = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(strings));//[卡莎, 泰坦, A, 洛]
//把集合的全部元素移动到另一个集合中
Collection<String> c2 = new ArrayList<String>();
c2.addAll(c);
System.out.println(c2);//[卡莎, 泰坦, A, 洛]
}
4. Collection 集合的遍历方式
(1) 迭代器:迭代器是用来遍历集合的专用方式(数组没有迭代器),Java中最常用的迭代器是Iterator
方法名 | 说明 |
Iterator<E> iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素 |
Boolean hasNext() | 询问当前位置是否有元素存在,存在返回true 不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象指向下一个元素 |
public static void main(String[] args) {
//iterator
Collection<String> c = new ArrayList();
c.add("卡莎");
c.add("泰坦");
c.add("洛");
//Iterator<E> iterator()返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素
Iterator<String> iterator = c.iterator();
//Boolean hasNext() 询问当前位置是否有元素存在,存在返回true 不存在返回false
//E next() 获取当前位置的元素,并同时将迭代器对象指向下一个元素
/*System.out.println(iterator.next());//卡莎
System.out.println(iterator.next());//泰坦
System.out.println(iterator.next());//洛
System.out.println(iterator.next());//NoSuchElementException 异常*/
while (iterator.hasNext()) {
String ele = iterator.next();
System.out.println(ele);//卡莎 泰坦 洛
}
}
(2) 增强for:既可以用来遍历集合,也可以用来遍历数组
格式:for(元素的数据类型 变量名 : 数组或集合){ }
本质上就是迭代器遍历集合的简化写法
public static void main(String[] args) {
Collection<String> c = new ArrayList();
c.add("卡莎");
c.add("泰坦");
c.add("洛");
for (String s : c){
System.out.println(s);//卡莎 泰坦 洛
}
String[] ss = {"飞机","奥恩"};
for (String s : ss){
System.out.println(s);//飞机 奥恩
}
}
(3) lambda表达式:JDK8 开始的Lambda表达式,提供了一种更简单、更直接的方式来遍历集合
default void forEach(Consumer<? super T> action) 结合lambda遍历集合
public static void main(String[] args) {
Collection<String> c = new ArrayList();
c.add("卡莎");
c.add("泰坦");
c.add("洛");
// default void forEach(Consumer<? super T> action) 结合lambda遍历集合
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);//卡莎 泰坦 洛
}
});
c.forEach((String s) ->{
System.out.println(s);//卡莎 泰坦 洛
}
);
c.forEach((s) ->
System.out.println(s)//卡莎 泰坦 洛
);
c.forEach(s -> System.out.println(s));//卡莎 泰坦 洛
c.forEach(System.out::println);
}
5. List集合
(1) List集合支持索引,除了Collection的方法,多了很多与索引相关的方法。包括ArrayList集合和LinkedList集合
方法名称 | 说明 |
void add(int index, E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index, E element) | 修改指定索引出的元素,返回被修改的元素 |
E get(int index) | 返回指定索引出的元素 |
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("卡莎");
list.add("泰坦");
list.add("泰坦");
list.add("洛");
System.out.println(list);//[卡莎, 泰坦, 泰坦, 洛]
//void add(int index, E element) 在此集合中的指定位置插入指定的元素
list.add(3, "霞");
System.out.println(list);//[卡莎, 泰坦, 泰坦, 霞, 洛]
//E remove(int index) 删除指定索引处的元素,返回被删除的元素
System.out.println(list.remove(2));//泰坦
System.out.println(list);//[卡莎, 泰坦, 霞, 洛]
//E set(int index, E element) 修改指定索引出的元素,返回被修改的元素
System.out.println(list.set(2, "伊泽"));//霞
//E get(int index) 返回指定索引出的元素
System.out.println(list.get(2));//伊泽
}
(2) List集合的遍历方式
① for循环(因为List集合有索引)
② 迭代器
③ 增强for循环
④ Lambda表达式
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("卡莎");
list.add("泰坦");
list.add("泰坦");
list.add("洛");
//① for循环(因为List集合有索引)
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//② 迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//③ 增强for循环
for (String string : list) {
System.out.println(string);
}
//④ Lambda表达式
list.forEach(System.out::println);
}
6. ArrayList集合的底层原理
(1) ArrayList与LinekdList都是 有序、可重复、 有索引,但底层采用的数据结构不同,应用场景也不同。
(2) ArrayList的底层原理:基于数组实现的。利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组;添加第一个元素时,底层会创建一个新的长度为10的数组;存满时,会扩容1.5倍;如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准;
① 查询速度快(根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同
② 删除效率低:可能需要把后面很多的数据进行前移
③ 添加效率低:可能需要把后面很多的数据后移,再添加元素;或者可能需要进行数组的扩容
7. LinekdList集合的底层原理
(1) ArrayList的底层原理:基于双链表实现的;链表中的节点都是独立的对象,在内存中时不连续的,每一个节点包含数据值和下一个节点的地址。
(2) 特点:
① 查询慢,无论查询哪个数据都要从头开始找。
② 增删相对快,对首尾元素进行增删改查的速度是极快的。
(3) LinekdList新增了很多首尾操作的特有方法
方法名称 | 说明 |
public void addFirst(E e) | 将指定的元素追加到列表的开头 |
public void addLast(E e) | 将指定的元素追加到列表的末尾 |
public E getFirst() | 返回第一个元素 |
public E getLast() | 返回最后一个元素 |
public E removeFirst() | 删除并返回第一个元素 |
public E removeLast() | 删除并返回最后一个元素 |
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("卡莎");
list.add("泰坦");
list.add("泰坦");
list.add("洛");
System.out.println(list);//[卡莎, 泰坦, 泰坦, 洛]
//public void addFirst(E e) 将指定的元素追加到列表的开头
list.addFirst("张飞");
System.out.println(list);//[张飞, 卡莎, 泰坦, 泰坦, 洛]
//public void addLast(E e) 将指定的元素追加到列表的末尾
list.addLast("豆芽");
System.out.println(list);//[张飞, 卡莎, 泰坦, 泰坦, 洛, 豆芽]
//public E getFirst() 返回第一个元素
System.out.println(list.getFirst());//张飞
//public E getLast() 返回最后一个元素
System.out.println(list.getLast());//豆芽
//public E removeFirst() 删除并返回第一个元素
System.out.println(list.removeFirst());//张飞
//public E removeLast() 删除并返回最后一个元素
System.out.println(list.removeLast());//豆芽
}
8. Set集合
(1) 特点:添加的元素是无序(添加数据的顺序和获取数据出的数据顺序不一样)、不重复、无索引
(2) Set 要用到的常用方法,基本上就是Collection提供的,自己几乎没有新增的方法
9. HashSet集合
(1) 无序、不重复、无索引
底层原理:
(2) 哈希值:就是一个int类型的数值,Java中每一个对象都有一个哈希值。都可以通过Objectl类提供的hashCode方法获取对象的哈希值。public int hashCode()
① 同一个对象多次调用hashCode()方法返回的哈希值是相同的
② 不同的对象,他们的哈希值一般不相同,但也有可能会相同(哈希碰撞)
(3) HashSet 是基于哈希表实现的。哈希表是一种增删改查数据性能都较好的数据结构。JDK8之前哈希表=数组+链表;JDK8之后:数组+链表+红黑树
(4) DK8开始,当链表的长度超过8,且数组长度>=64时,自动将链表转成红黑树
(5) 如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法
public class Student {
private int id;
private String name;
private Double[] grades;
public Student() {
}
public Student(int id, String name, Double[] grades) {
this.id = id;
this.name = name;
this.grades = grades;
}
// idea快捷键生成,类中右键选择Generate->equals() and hashCode()
@Override
public int hashCode() {
return Objects.hash(id, name, Arrays.hashCode(grades));
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id && Objects.equals(name, student.name) && Objects.equals(grades, student.grades);
}
}
10. LinkedHashSet集合
(1) 有序(添加元素的顺序和获取元素的顺序一样)、不重复、无索引
底层原理:
(2) 基于哈希表(数组、链表、红黑树)实现的;但是,他的每个元素都额外多了一个双链表的机制记录它前后元素的位置(更占内存)
11. TreeSet集合
(1) 按照大小默认升序排序、不重复、无索引
底层原理:
(2) 基于红黑树实现的排序
(3) 注意:
① 对于数值类型:Integer、Double,默认按照数值大小进行排序
② 对于字符串类型:默认按照字符的编号升序排序
③ 对于自定义类型(如Student)对象,TreeSet默认是无法直接排序的
④ 自定义排序规则:TreeSet集合存储自定义类型的对象时,必须指定排序规则(有两种):
规则1:让自定义类实现Comparable接口,重写里面的CompareTo()方法
规则2:通过调用TreeSet集合有参构造器,可以设置Comparator对象(比较器对象)--public TreeSet(Comparator<? Super E> comparator)
如果规则1和规则2同时使用,默认就近原则(规则2)
//方式1. Comparable:让该类对象实现Comparable(比较规则)接口,然后重写compareTo方法,自己制定比较规则
public class Student implements Comparable<Student>{
private String name;
private int age;
private double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
@Override
public int compareTo(Student o) {
//左边对象 大于 右边对象 返回正整数
//左边对象 小于 右边对象 返回负整数
//左边对象 等于 右边对象 返回0
//如按照成绩排序 默认升序
/*if (this.score > o.score) {
return 1;
}else if (this.score < o.score) {
return -1;
}else {
return 0;
}*/
return Double.compare(this.getScore(), o.getScore());//升序
//降序的话:
//左边对象 大于 右边对象 返回负整数
//左边对象 小于 右边对象 返回正整数
//左边对象 等于 右边对象 返回0
/* if (this.score > o.score) {
return -1;
}else if (this.score < o.score) {
return 0;
}else {
return 0;
}
return Double.compare(o.getScore(), this.getScore());//降序
*/
}
}
public static void main(String[] args) {
//HashSet
Set<String> set = new HashSet();//无序 不重复 无索引
set.add("1");
set.add("2");
set.add("3");
set.add("4");
set.add("1");
set.add("5");
set.add("2");
System.out.println(set);//
//LinkedHashSet
Set<String> set1 = new LinkedHashSet();//有序 不重复 无索引
set1.add("3");
set1.add("2");
set1.add("1");
set1.add("4");
set1.add("5");
System.out.println(set1);
//TreeSet
Set<String> set3 = new TreeSet();
set3.add("3");
set3.add("1");
set3.add("2");
set3.add("6");
set3.add("5");
System.out.println(set3);
Set<Integer> set4 = new TreeSet();
set4.add(1);
set4.add(7);
set4.add(5);
set4.add(6);
set4.add(2);
set4.add(5);
System.out.println(set4);
//自定义对象TreeSet
Student student = new Student("卡莎", 18, 99);
Student student1 = new Student("泰坦", 19, 93);
Student student2 = new Student("伊泽", 16, 98);
Student student3 = new Student("璐璐", 16, 98);
Set<Student> set5 = new TreeSet();
set5.add(student);
set5.add(student1);
set5.add(student2);
set5.add(student3);
//[Student{name='泰坦', age=19, score=93.0}, Student{name='伊泽', age=16, score=98.0}, Student{name='卡莎', age=18, score=99.0}]
System.out.println(set5);//由于伊泽和璐璐 分数98相同 后一个不存了
Set<Student> set6 = new TreeSet(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge()- o2.getAge();
}
});
set6.add(student);
set6.add(student1);
set6.add(student2);
set6.add(student3);
//[Student{name='伊泽', age=16, score=98.0}, Student{name='卡莎', age=18, score=99.0}, Student{name='泰坦', age=19, score=93.0}]
System.out.println(set6);
}
二. 集合的并发修改异常问题
1. 集合的并发修改异常:使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常
2. 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常
3. 解决方法:
① 如果使用迭代器遍历集合,用迭代器自己的删除方法删除数据即可
② 如果能用for循环遍历时,可以倒着遍历并删除;或者从前往后遍历,删除元素后进行i--;
public static void main(String[] args) {
//集合的并发修改异常问题
ArrayList<String> list = new ArrayList<String>();
list.add("泰坦");
list.add("卡莎");
list.add("王莎");
list.add("洛");
list.add("伊泽");
list.add("赵莎");
list.add("璐璐");
list.add("孙莎");
System.out.println(list);
//迭代器 ConcurrentModificationException异常
Iterator<String> iterator = list.iterator();
/*while (iterator.hasNext()) {
String s = iterator.next();
if (s.contains("莎")){
list.remove(s);//ConcurrentModificationException
}
}*/
//解决方法iterator.remove();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.contains("莎")){
iterator.remove();//删除迭代器当前遍历到的数据,每删除一个数据后,相当于i--
}
}
System.out.println(list);
ArrayList<String> list2 = new ArrayList<String>();
list2.add("泰坦");
list2.add("卡莎");
list2.add("王莎");
list2.add("洛");
list2.add("伊泽");
list2.add("赵莎");
list2.add("璐璐");
list2.add("孙莎");
//for循环
for (int i = 0; i < list2.size(); i++) {
String s = list2.get(i);
if (s.contains("莎")){
list2.remove(i);
}
}
//出现bug 没有删除完
System.out.println(list2);//[泰坦, 王莎, 洛, 伊泽, 璐璐]
//解决方法1 i--
for (int i = 0; i < list2.size(); i++) {
String s = list2.get(i);
if (s.contains("莎")){
list2.remove(i);
i--;
}
}
//解决方法2 倒着删
for (int i = list2.size() - 1; i > 0; i--) {
String s = list2.get(i);
if (s.contains("莎")){
list2.remove(i);
}
}
ArrayList<String> list3 = new ArrayList<String>();
list3.add("泰坦");
list3.add("卡莎");
list3.add("王莎");
list3.add("洛");
list3.add("伊泽");
list3.add("赵莎");
list3.add("璐璐");
list3.add("孙莎");
//使用正确for循环遍历集合并删除数据,没有办法解决ConcurrentModificationException异常
//ConcurrentModificationException异常
for (String s : list3) {
if (s.contains("莎")){
list3.remove(s);
}
}
//使用正确forEach循环遍历集合并删除数据 没有办法解决ConcurrentModificationException异常
//ConcurrentModificationException异常
list.forEach(name -> {
if (name.contains("莎")){
list3.remove(name);
}
});
}
标签:Set,Java,list,List,System,add,泰坦,println,out
From: https://blog.csdn.net/zeijiershuai/article/details/144858299