目录
Collections集合工具类(操作集合的各种方法,全是静态方法。相对应的,操作数组的各种方法为Arrays工具类)
List集合
List接口介绍:
java.util.List
接口 extends Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List
接口的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List接口的特点:
-
它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
-
它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
-
集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
tips:我们在基础班的时候已经学习过List接口的子类java.util.ArrayList类,该类中的方法都是来自List中定义。
List接口中常用方法:
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
-
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。 -
public E get(int index)
:返回集合中指定位置的元素。 -
public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。 -
public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回被替换的元素。
注意:操作索引的时候,一定要防止索引越界异常。编译报错,运行报错。
List集合遍历有三种遍历方式:
1.使用普通for循环。
2.使用迭代器。
3.使用增强for循环。
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
public class ListFor {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("赵丽颖");
list.add("杨颖");
String remove = list.remove(3);
String get = list.get(0);
String setName = list.set(2, "麦迪娜");
System.out.println("删除的是:"+remove+" 第一个是:"+get+" 被替换的是:"+setName);
System.out.println("最新角逐名单:"+list);
// 三种循环方式
System.out.println("方式一:使用普通for循环");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("方式二:使用Iterator");
Iterator<String> it = list.iterator();
while(it.hasNext()){
String next = it.next();
System.out.println(next);
}
System.out.println("方式三:使用增强型for");
for (String s : list) {
System.out.println(s);
}
}
}
ArrayList集合
介绍
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以
java.util.ArrayListArrayList
是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求(应该查询多的时候使用该集合,增删多的时候不应该使用该集合),并不严谨,这种用法是不提倡的。
LinkedList集合
java.util.LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。(了解即可)
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:
-
public void addFirst(E e)
:将指定元素插入此列表的开头。 -
public void addLast(E e)
:将指定元素添加到此列表的结尾。 -
public E getFirst()
:返回此列表的第一个元素。 -
public E getLast()
:返回此列表的最后一个元素。 -
public E removeFirst()
:移除并返回此列表的第一个元素。 -
public E removeLast()
:移除并返回此列表的最后一个元素。 -
public E pop()
:从此列表所表示的堆栈处弹出一个元素。 -
public void push(E e)
:将元素推入此列表所表示的堆栈。 -
public boolean isEmpty()
:如果列表不包含元素,则返回true。
Vector集合
Vector
类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector
的大小可以根据需要增大或缩小,以适应创建 Vector
后进行添加或移除项的操作。与新 collection 实现不同,Vector
是同步的。也就是单线程的,这是早期的1.0版本的。
Set集合
介绍:
java.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口,它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了。
Set
接口中元素的特点:
与List
接口不同的是
(1)没有索引,没有带索引的方法,因此不能使用普通的for循环遍历。
(2)不允许出现重复。
Set
集合有多个子类,这里我们介绍其中的java.util.HashSet
、java.util.LinkedHashSet
这两个集合。
tips:Set集合取出元素的方式可以采用:迭代器、增强for。
HashSet集合
java.util.HashSet
集合 implements Set
接口,是一个实现类。
HashSet集合特点:
1.没有索引,没有带索引的方法,因此不能使用普通的for循环遍历。
2.不允许出现重复元素。
3.是一个无序的集合,存储元素和取出元素的顺序有可能不一致。
4.底层是一个哈希表结构(java.util.HashMap
支持查询速度非常快)
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetTest {
public static void main(String[] args) {
Set<Integer> list = new HashSet<>();
list.add(10);
list.add(30);
list.add(20);
list.add(10); // 重复元素
// 使用迭代器
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
Integer next = iterator.next();
System.out.println(next); // 20 10 30 无序
}
// 使用增强for
for (Integer integer : list) {
System.out.println(integer); // 20 10 30无序
}
}
}
HashSet集合存储数据的结构(哈希表)
哈希值:是一个十进制的整数,由操作系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到的地址,不是数据实际存储的物理地址)。
在Object类有一个方法,可以获取对象的哈希值。int hashCode()方法。
哈希冲突:元素不同,但是哈希值相同。
哈希表:
在JDK1.8之前,哈希表底层采用数组+链表实现。数组结构将元素进行了分组,相同哈希值的元素是一组,放在同一个哈希值下面。因为数组存储的是哈希值。然后使用链表处理哈希冲突,同一hash值的链表都存储在一个链表里。但是当位于一个链表中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说
jdk1.8版本之前:哈希表 = 数组 + 链表
jdk1.8版本之后:哈希表 = 数组 + 红黑树
哈希表的特点:速度快!
Set集合存储元素不重复的原理:
HashSet
是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
与equals
方法。
存储流程图:
HashSet存储自定义类型元素
给HashSet中存放自定义类型元素时,一定需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一 。
// 自定义数据类型Student,一定要重写equals和hashCode方法
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
// 重写了equals方法
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
// 重写了hashCode方法
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
// 测试存储自定义类型数据,能使得结果不重复。
public class HashSetDemo2 {
public static void main(String[] args) {
//创建集合对象 该集合中存储 Student类型对象
HashSet<Student> stuSet = new HashSet<Student>();
//存储
Student stu = new Student("于谦", 43);
stuSet.add(stu);
stuSet.add(new Student("郭德纲", 44));
stuSet.add(new Student("于谦", 43));
stuSet.add(new Student("郭麒麟", 23));
stuSet.add(stu);
for (Student stu2 : stuSet) {
System.out.println(stu2);
}
}
}
执行结果: // 不会重复于谦
Student [name=郭德纲, age=44]
Student [name=于谦, age=43]
Student [name=郭麒麟, age=23]
LinkedHashSet集合
java.util.LinkedHashSet集合 extends HashSet集合
LinkedHashSet集合特点:
底层是一个哈希表(数组+链表/红黑树)+链表。多了一条链表是为了记录元素的存储顺序,保证元素有序。
import java.util.HashSet;
import java.util.LinkedHashSet;
public class LinkedHashSetTest {
public static void main(String[] args) {
// 使用HashSet集合
HashSet<String> set = new HashSet<>();
set.add("古力娜扎");
set.add("赵丽颖");
set.add("迪丽热巴");
set.add("迪丽热巴");
System.out.println(set); // [赵丽颖, 迪丽热巴, 古力娜扎] 存储是无序的
// 使用LinkedHashSet集合
LinkedHashSet<String> linedSet = new LinkedHashSet<>();
linedSet.add("古力娜扎");
linedSet.add("赵丽颖");
linedSet.add("迪丽热巴");
linedSet.add("迪丽热巴");
System.out.println(linedSet); // [古力娜扎, 赵丽颖, 迪丽热巴] 存储是有序的
}
}
Collections集合工具类(操作集合的各种方法,全是静态方法。相对应的,操作数组的各种方法为Arrays工具类)
java.utils.Collections
是集合工具类,用来对集合进行操作。部分方法如下:
-
public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中添加多个元素,不用像之前的一个一个add添加进集合中了。 -
public static void shuffle(List<?> list) 打乱顺序
:打乱集合顺序。 -
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。 -
public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。
public static <T> void sort(List<T> list)
方法使用前提:
被排序的list集合里面存储的元素,必须实现Comparable接口,重写接口中的compareTo方法定义排序的规则。
排序规则:通常规定,对于两个元素x
和y
,原来的顺序是x在前y在后。如果认为x < y
,则返回-1
,如果认为x == y
,则返回0
,如果认为x > y
,则返回1
,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。
-
返回1,表示前后两个相邻元素要换位置,-1和0不换。
比如:如果x = 10 ,y = 5 x > y return 1。
当x>y的时候要交换位置,那么就是升序排序。
-
如果x = 10,y = 20 y > x return 1.表示当y>x的时候要交换位置,那么表示降序排序。
this.age永远是当前的元素,o.age是下一个元素。当为正的时候,即return 1.需要交换顺序。其余情况则不需要交换顺序。
通过上面的规则:
当 this.age>o.age时,年龄按升序排序。 所以当return this.age-o.age时,年龄升序排序。
当 this.age<o.age时,年龄按降序排序 。 所以当return o.age-this.age时,年龄降序排序。
Comparator和Comparable的区别,也就是方法三和方法四的区别:
Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较规则compareTo方法。
Comparator:相当于找一个第三方的裁判,比较两个,里面的参数原来的顺序是o1在前o2在后。
// 定义一个Person类,其中重写了toString方法,实现Comparable接口并重写compareTo方法
public class Person implements Comparable<Person> {
private String name;
private int age;
private int nice; // 美貌值 0-10
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", nice=" + nice +
'}';
}
public Person() {
}
public Person(String name, int age, int nice) {
this.name = name;
this.age = age;
this.nice = nice;
}
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 int getNice() {
return nice;
}
public void setNice(int nice) {
this.nice = nice;
}
@Override // 如果没有重写该方法就不能对Person排序。
public int compareTo(Person o) {
// return 0; //原来的代码恒返回0,表示不需要交换未知也就是不进行排序返回原序列。
if (this.age > o.age) { // 只对年龄进行了排序
return 1; // 返回正序。原来的顺序:this.age是当前元素,o.age是下一个元素。return 1需要交换位置。
}
if (this.age < o.age) {
return -1;
}
return 0;
// 等效于下面这句
// return this.age-o.age; // this.age永远是当前的元素,o.age是下一个元素。当为正的时候,即return 1.需要交换顺序。其余情况则不需要交换顺序。
/* 当 this.age>o.age时,年龄按升序排序。
所以return this.age-o.age时,年龄升序排序。
当 this.age<o.age时,年龄按降序排序
所以当return o.age-this.age时,年龄降序排序。
*/
}
}
// Collections集合工具类的使用
import java.util.Collections;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.List;
public class LinkedHashSetTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list,10,23,11,5); // 一次加入多个元素,这个可以Collection集合,不局限于List集合
System.out.println(list); // [10, 23, 11, 5]
Collections.sort(list); // 默认规则排序,从小到大
System.out.println(list); // 升序 [5, 10, 11, 23]
// 使用第三方规则
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1; // 第三方规则,从大到小
}
});
System.out.println(list); // 降序 [23, 11, 10, 5]
Collections.shuffle(list); // 打乱顺序
System.out.println(list); // [11, 23, 5, 10]
// List集合中的元素是自定义类型
List<Person> persons = new ArrayList<>();
persons.add(new Person("赵丽颖",20,7));
persons.add(new Person("迪丽热巴",18,9));
persons.add(new Person("古力娜扎",19,8));
// 被排序的list集合里面存储的元素,必须实现Comparable接口,重写接口中的compareTo方法定义排序的规则。
Collections.sort(persons); // 进行排序,排序规则已经重写,只对年龄排序,是升序排序。
System.out.println(persons); // [Person{name='迪丽热巴', age=18, nice=9}, Person{name='古力娜扎', age=19, nice=8}, Person{name='赵丽颖', age=20, nice=7}]
// 还可以使用第三方的,不需要去继承Comparable类和重写compareTo方法
Collections.sort(persons, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getNice()-o2.getNice();// 美貌值从小到大排序
}
});
System.out.println(persons); // [Person{name='赵丽颖', age=20, nice=7}, Person{name='古力娜扎', age=19, nice=8}, Person{name='迪丽热巴', age=18, nice=9}]
}
}