万字总结——Java单列集合和Collections工具类
前言
很扯了,刚写完前言就结束了,我还没写美。平时也不怎么写博客,这是没被同质化拷打啊。但是感觉没那么有思考的知识可能没必要写🤔。那就写吧,突然学了Java的这些个集合,脑子都混了,不知道哪些集合该用到哪,什么时候该写这个Comparable或者什么时候写Comparator,我尽量写,如果今天写不完就分两篇写,我尽量哈⛽️
正文
写的范围
算了,这次就从这个单列集合开始吧,这里就介绍一下List和Set和这个Collection吧,下一篇来介绍这个Map以及这些集合的一些原理和集合的这个工具类Collections。
Collection
这是什么?
这个是一个接口,是那些Set,List这些单列集合的祖宗,这些集合都继承它,在java.util包里面
—那问题就来了:这个和那个Object什么关系?
——那当然是父子关系来,Object是所有类和所有接口的祖宗,那肯定Collection就是它后代,这是第一个可能会比较迷的东西(反正我迷了)
它有哪些方法?
- add方法
这里可以直接添加一个元素(这里是一个泛型对象e,就是你Collection后面写的泛型,这里是String),返回值是一个boolean,如果添加成功就返回true,添加失败返回false
上代码!!!
注意
1.这里的Collection是一个接口,无法实例化对象,所以这里接收这里ArrayList的对象
2. 集合可以直接打印,这里底层都写好了格式Collection<String> collection = new ArrayList<>(); //这里接收一下返回值(平时不接收) boolean b = collection.add("a"); //打印返回值 System.out.println(b); System.out.println(collection); - clear方法
返回值和参数都是void,作用是清空集合的所有元素collection.clear(); System.out.println(collection); - remove方法
这里属于见名知义了,这里的意思就是删除,不过这个删除的参数是一个Collection的泛型里面的元素(后面有参数是下标的),返回值是一个布尔类型的,这里删除成功就返回truecollection.add("b"); collection.add("c"); collection.remove("b"); System.out.println(collection); - contains方法
这里是判断集合里面是否有这个对象,这个挺重要的,因为后面的if语句条件很可能就是它,里面传一个Object的对象,返回值是布尔类型的,如果存在就返回true
这里我刚刚出来了个问题——就是这里为什么有参数是泛型有参数是Object,后来我才知道为啥,这里的泛型是为了限制你操作的类型(这里要和集合定义的时候泛型传递的类型一致),而这里的Object对象才是可以传递任意类型可以让它判断一下是不是一致的
- isEmpty方法/size方法
这里我直接一笔带过了,isEmpty返回boolean类型,如果空的就返回true,然后这里size返回int类型数字if(collection.isEmpty()){ System.out.println("集合为空"); } else{ System.out.println("集合不为空"); System.out.println(collection.size()); }
遍历(比较重要)
这里的遍历确实比较重要,这里提供了三种遍历方式分别是
- 迭代器
- 增强for
- forEach(lambda表达式)
我直接用代码演示了
迭代器
这里使用集合自带的iterator方法来创建迭代器对象,并且调用迭代器自己 的这个hasNext方法和next方法
- 注意
这里迭代器内部有一个索引,索引走到最后就无法遍历了,只能再创建迭代器进行遍历,当然这里List迭代器除外,不过它那个方法也是感觉一般般吧😂
//创建迭代器对象(注意一定要加上泛型,不然下面的元素都要强转,特别是后面Map的键值对,很麻烦)
Iterator<String>it = collection.iterator();
//hasNext()方法判断这个地方有没有元素,有则返回true,没有则返回false
while(it.hasNext()){
//迭代器的next()方法返回这个元素并且让索引指向下一个元素
System.out.println(it.next());
}
增强for
这里是JDK5之后出现的,底层就是这个迭代器,这个语法和cpp也是相似的
这里就像从这个collection里面一个个抽取这个s,然后打印这个s
for(String s : collection){
System.out.println(s);
}
forEach(lambda)
本质是集合的forEach方法调用匿名内部类,这个匿名类调用Consumer接口,重载accept函数,打印这个s
collection.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
- 注意
然后我们就发现了这个Consumer是一个函数式接口,一旦牵扯到函数式编程,我们就可以用lambda表达式来简化代码了,这里就是简化规则
我直接给上面的那个改个刀,开刀!!!- new、继承的接口名、泛型、方法名、方法的修饰符、返回值、和形参类型可以省略,如果形参只有一个,可以省略包裹参数的括号,参数和方法体中间用->连接。
//这里第一刀 collection.forEach((String s) -> { System.out.println(s); } );- 如果方法体内除了return的方法体只有一行,直接可以将方法体的括号和return和分号省略。但是要省略就一起省略
//最终形态 collection.forEach(s -> System.out.println(s));
总结
我比较建议使用这个增强for或者这个forEach方法了,特别是后面遍历Map的时候我感觉这个forEach非常牛逼了🐮
List
特点:有序、有索引、可重复
- 这里要提醒一下:刚刚Collection的那些个方法这个List集合都可以使用,毕竟是祖宗传下来的
- 这里List集合底下包含两种集合:ArrayList(动态数组)和LinkedList(链表),也是两种使用率比较高的数据结构了
- 不管是List还是ArrayList 或者LinkedList还是以后的什么Set和Map啊,这些个集合都在这个java.util包下,报错的时候可以看看有没有调用,这个以后就不提了
方法
Collection的方法它都能直接调用
- 构造方法
这里直接使用ArrayList的集合创建ArrayListList<String> list = new ArrayList<>(); - add方法
这里的特有add方法的返回值是void,这里的第一个参数是索引,第二个参数是泛型元素,这里会在你指定的索引处添加这个泛型元素List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); //这里就是这个方法 list.add(0,"c"); for(String s : list){ System.out.println(s); } - set方法
这里的作用是修改,这里的返回值是一个泛型元素,其实返回的就是你修改前的元素,第一个参数是索引,第二个参数是泛型元素ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add(0,"c"); list.set(0,"hehe"); for(String s : list){ System.out.println(s); } - get方法
这里的作用就是获取指定索引值的元素,这里的参数只有一个就是一个索引,这里返回一个泛型元素,这个方法也是我们可以普通for循环的底气,这里我直接在这个普通for循环来演示
遍历
这里除了Collection的三种遍历方式之后,这里还有另外两种遍历方式
普通for循环
因为有get方法,直接就可以for循环了
for(int i = 0;i<list.size();i++)
{
System.out.print(list.get(i)+" ");
}
列表迭代器
这个迭代器和之前那个迭代器的区别就是:原来的迭代器到了最后就不能用了,这个迭代器有一个方法可以从后往前遍历,让指针指向最前面,然后你再这个可以从前往后,而且它可以在遍历的时候增加修改或者删除元素,不过咱只说遍历
//从前往后
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
System.out.println(it.next());
}
//从后往前
while(it.hasPrevious()){
System.out.println(it.previous());
}
写个List给我累够呛
ArrayList
这里的ArrayList的作用就是继承List,这个是可变数组,没有比较常见的特有方法,所以当List看就行
ArrayList<String> al = new ArrayList<>();
LinkedList
- 这里的LinkedList就很有说法了,因为它有很多的特有方法,除了特有方法,其他都可以直接用List的方法
- 底层是双链表,这里增删快改查慢
特有方法
- addFirst
这里就是头插法,参数是一个泛型元素,返回值是void,一会直接一个代码给这些个方法都用了 - addLast
参数和返回值和上面这个一样,这个是尾插法 - getFirst
获取链表的头节点,这里参数void,返回值是一个泛型元素 - getLast
获取尾节点,参数和上面这个一样 - removeFirst
删除头节点,返回被删除的头节点元素 - removeLast
删除尾节点,返回被删除的尾节点元素LinkedList<String> list = new LinkedList<>(); list.addFirst("wang"); list.addFirst("hi"); list.addFirst("hehe"); list.addLast("hello"); System.out.println(list); System.out.println(list.getFirst()); System.out.println(list.getLast()); System.out.println(list.removeFirst()); System.out.println(list.removeLast()); System.out.println(list);
Set
特点:无序、无索引、不可重复
Set集合下面的集合
HashSet:哈希表(无序、无索引、不可重复)
LinkedHashSet:哈希表加链表(有序、无索引、不可重复)
TreeSet:红黑树(无序、无索引、不可重复)
注意:这里的无序是指不按照添加的顺序
遍历
这里的遍历的方法和Collection集合遍历方法一样
HashSet
底层是哈希表,后面只要遇到哈希表就和这个hashcode和equals方法有关
这个集合有一个特点——这个集合在这个存自定义类的时候要重写hashcode和equals方法
这个hashcode和equals方法(因为哈希表的本质是这个通过哈希值找到对象)
如果是java自带的类型他都给你写好了,但是如果你是这个自定义类型,这里算哈希值的方法就是通过地址值来计算,但是我们想区分两个这个元素肯定不能用这个地址值啊,肯定是我们自己设定的属性值,所以这个时候我们就要重写这个hashcode和equals方法了
说实在的这里的这个hashcode和equals教的是直接用idea生成
//学生类
class Student implements Comparable<Student>
{
private String name;
private int age;
//构造方法
//无参构造方法
//这里省略无参构造方法
//有参构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//getter方法
//这里省略getter方法
//setter方法
//这里省略setter方法
//重写toString方法
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//重写equals方法
@Override
public boolean equals(Object obj) {
//这里判断this和obj是否为同一个对象
//如果是同一个对象,返回true
if (this == obj) {
return true;
}
//这里判断obj是否为null
//如果为null,返回false
//getClass判断是不是一个类
if (obj == null || getClass() != obj.getClass()) {
return false;
}
//这里将obj转换为Student类型
Student student = (Student) obj;
//这里按照姓名和年龄比较
return Objects.equals(name, student.name) && age == student.age;
}
//重写hashCode方法
@Override
public int hashCode() {
//这里按照姓名和年龄计算hashCode值
return Objects.hash(name, age);
}
//重写compareTo方法
@Override
public int compareTo(Student o) {
//这里按照年龄比较
return this.age - o.age;
}
}
LinkedHashSet
这里其实和HashSet差不多,只是这里有一个双链表来记录这里这里添加的先后顺序
//创建LinkedHashSet对象
LinkedHashSet<Student> ls1 = new LinkedHashSet<>();
Student s1 = new Student("张三",18);
Student s2 = new Student("李四",19);
Student s3 = new Student("王五",20);
Student s4 = new Student("张三",18);
//添加学生对象到LinkedHashSet中
ls1.add(s1);
ls1.add(s3);
ls1.add(s2);
ls1.add(s4);
//这里是按照添加顺序打印的
System.out.println(ls1);
对比
这里你会发现这里HashSet会自己有一套排序顺序,而这里的LinkedHashSet会按照这里的添加顺序打印(但底层的存储方式还是HashSet一样的,只不过这里的链表保留了这个排列顺序)
HashSet<String> set = new HashSet<>();
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
set.add("wang");
set.add("hehe");
set.add("hi");
set.add("hello");
System.out.println(set);
linkedHashSet.add("wang");
linkedHashSet.add("hi");
linkedHashSet.add("hehe");
linkedHashSet.add("hello");
System.out.println(linkedHashSet);
TreeSet
这里底层是红黑树,总结这里只要牵扯到这个红黑树,这里就会和Comparable和这个Comparator扯上关系
既然是红黑树就扯到这个排序,既然是排序那就肯定要比大小
比较方法
- 数值类型就直接按照大小来比大小
- 如果是字符串类型就按照ASCii码值来比较大小
默认比较方法
调用时机:在定义类的时候。这里调用这个Comparable接口重载compareTo函数
注意:
- 这里第一个就是导包问题
- 第二个就是形参的逻辑关系,this是待加入的元素,这里的o是红黑树里的节点,所以这里的返回值—1.如果等于0,默认这里已经有这个节点了。2.如果大于0,说明这里的应该在节点右边。3.如果小于0,说明在节点左边
- 这里的升序和降序控制:如果o.age被减,说明是升序,如果o.age是减数,说明是降序(通过被减数和减数的关系来比节点大的放在左边还是右边)
说明:如果是o.age被减,那就是大的放左边(小于0),如果是o.age做减数,那就是大的放右边(大于0)
//学生类
class Student implements Comparable<Student>
{
//这里省略JavaBean类里面的方法和参数
//重写compareTo方法
@Override
public int compareTo(Student o) {
//这里按照年龄比较
return this.age - o.age;
}
}
比较器比较
这里就是这个在代码里面创建对象的时候调用了(匿名内部类+重载函数),这里的返回值和上面一样
//这里的逻辑就是先根据字符串长度排序,如果一样就按照这个ASCii值进行排序
//创建TreeSet对象
TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//这里o1是节点,o2是新元素
if(o1.length()!=o2.length()){
//这里根据长度进行排序(升序)
return o1.length()-o2.length();
}else
{
//这里根据字母顺序进行排序(如果长度一样)
//这里的方法是字符串的比较方法
return o1.compareTo(o2);
}
}
//添加字符串到TreeSet中
ts2.add("hello");
ts2.add("apple");
ts2.add("world");
ts2.add("java");
ts2.add("python");
System.out.println(ts2);
使用场景
这是黑马总结的使用场景
Collections工具类
这个工具类适用于单列集合(注意是单列集合!!!),提供了操控单列集合的一些方法,这里我会将这些方法一一列举出来。(注意到底是所有集合都可以使用还是只有List集合才能使用!!!)
常见方法
- addAll方法
这个方法的特点就是可以批量添加元素,第一个参数是这个集合,第二个参数就是任意数量的这个泛型元素。
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi");
System.out.println(list);
- shuffle方法
这个方法是打乱List集合的方法,比如我们接着上面直接写下面,这里返回值和这个参数都是void
Collections.shuffle(list);
System.out.println(list);
- sort方法
这个方法是排序方法,这个方法有两个重载的版本
- 默认版本(这里按照Java写好的方法进行排序)
这里参数是List集合,这里字符串默认的排序是按照首字母进行排序
Collections.sort(list);
System.out.println(list);
- 自定义版本
这里多一个比较器参数对象,这里可以根据比较器的对象来进行自定义的一个排序
这里的自定义排序方式是按照字符串长度升序排序
Collections.sort(list,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
System.out.println(list);
- binarySearch方法
这个方法的作用是查找,底层是二分查找。这里的第一个参数是一个有序List集合(这里一定要保证有序!!!),第二个参数是集合里面的值。
这里的返回值很有意思,如果找到了就返回这里的索引,如果没找到就返回这里的-(插入点)-1
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi","apple");
Collections.sort(list);
System.out.println(list);
System.out.println(Collections.binarySearch(list, "wang"));
System.out.println(Collections.binarySearch(list, "hehe"));
System.out.println(Collections.binarySearch(list, "hi"));
System.out.println(Collections.binarySearch(list, "apple"));
- copy方法
这里覆盖一个List集合到另一个List集合,第一个参数是目标集合,第二个参数是原始集合。这里要求目标集合必须有元素并且大于等于原始集合的数量,这样才可以覆盖
依托答辩的方法啊!!!😠
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi","apple");
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"zhangsan","lisi","wangwu","zhasan");
Collections.copy(list2, list);
System.out.println(list2);
这个才是正经的拷贝方法!!!
ArrayList<String> list2 = new ArrayList<>();
list2.addAll(list1);
- fill方法
这也是个神人方法,功能是在一个**有元素的List集合里面,**把这个集合里面的元素换成你自己的元素,这里第一个参数是一个List集合,第二个参数是你要添加的元素(是一个Object对象)
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi","apple");
Collections.fill(list, "hello");
System.out.println(list);
- max/min方法
这里很正常的求一个集合(这次不是List集合了!!!)的最大/最小值,参数是一个集合,返回值是最大/最小元素本身
默认版本
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,9,10,8,5,7,6,4,3,2);
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
自定义比较器版本
自定义类型在定义类的时候调用Comparable接口或者在创建对象的时候调用Comparator接口
- 这里我展示一下max的比较器
- 这里可以使用lambda简化,因为Comparator是函数式接口
System.out.println(Collections.max(list,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}));
- swap接口
这里交换List集合里面的元素,第一个参数是List集合,第二,三个参数都是下标
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);
Collections.swap(list, 0, 9);
System.out.println(list);
结束
那就如此结束吧,捋一遍确实我自己也清晰了,希望大家可以分清楚什么集合用什么方法,感谢观看
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)