Java中的Collection集合以及Collection集合实现类实例
Collection集合
- 集合概述
- 集合的体系特点
- Collection集合常用API
- Collection集合的遍历方式
- 方式一:迭代器
- 方式二:foreach/增强for循环
- 方式三:lambda表达式
- Collection集合存储自定义类型的对象
- List系列集合
- List集合特点、特有API
- List集合的遍历方式小结
- ArrayList集合的底层原理
- LinkedList集合的底层原理
- Set系列集合
- Set系列集系概述
- HashSet元素无序的底层原理:哈希表
- HashSet元素去重复的底层原理
- 实现类:LinkedHashSet
- 实现类:TreeSet
集合概述
集合和数组都是容器。
数组特点:
1、 数组定义完成并启动后,类型确定、长度固定。2、适合元素的个数和类型确定的业务场景,不适合做需要增删数据操作。
集合特点:
1、集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球。
2、集合非常适合做元素的增删操作。
问题1、数组和集合的元素存储的个数问题。
数组定义后类型确定,长度固定
集合类型可以不固定,大小是可变的。
问题2、数组和集合存储元素的类型问题。
数组可以存储基本类型和引用类型的数据。
集合只能存储 引用数据类型 的数据。
问题3、数组和集合适合的场景
数组适合做数据个数和类型确定的场景。
集合适合做数据个数不确定,且要做增删元素的场景。
集合的体系特点
集合类 体系结构
Collection单列集合,每个元素(数据)只包含一个值,Map双列集合,每个元素包含两个值(键值对)
Collection集合体系
Collection集合特点
List系列集合: 添加的元素是 有序、 可重复、 有索引。
ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合: 添加的元素是无序、不重复、无索引。
HashSet: 无序、不重复、无索引;LinkedHashSet: 有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。
集合对于泛型的支持
集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
Collection<String> lists = new ArrayList<String>();
Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。
Collection<int> lists = new ArrayList<>(); // 这种写法是错误的,int为基本数据类型如果集合中要存储基本类型的数据怎么办?
// 存储基本类型使用包装类
Collection<Integer> lists = new ArrayList<>();
Collection<Double> lists = new ArrayList<>();
问题1、集合的代表是?
Collection接口。
问题2、Collection集合分了哪2大常用的集合体系?
List系列集合:添加的元素是有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
问题3、如何约定集合存储数据的类型,需要注意什么?
集合支持泛型。
集合和泛型不支持基本类型,只支持引用数据类型。
测试代码:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;public class CollectionTest {public static void main(String[] args) {// List接口(实现类:ArrayList && LinkList)// 有序,可重复 --- > 有索引Collection list = new ArrayList(); // 多态的写法list.add("java");list.add("java");list.add("Mybatis");list.add(23);list.add(23);list.add(false);list.add(false);System.out.println(list.toArray());System.out.println(list);System.out.println(list.toString());// set接口(实现类HashSet,TreeSet) 无序 不重复 -- > 无索引Collection list1 = new HashSet();list1.add("java");list1.add("java");list1.add("Mybatis");list1.add(23);list1.add(23);list1.add(false);list1.add(false);System.out.println(list1);System.out.println(list1.toString());System.out.println("--------------------------------");// 集合和泛型都不支持基本数据类型Collection<String> list3 = new ArrayList<>(); // <包裹的为泛型>表示只能存储该引用类型的数据,其它类型不能进行存储list3.add("Java");list3.add("1234");
// list3.add(1);System.out.println(list3);Collection<Integer> list2 = new ArrayList<>();list2.add(34); // 自动装箱处理 int -- > Integerlist2.add(new Integer(23)); // 有参构造list2.add(34);System.out.println(list2);}
}
Collection集合常用API
Collection集合 :Collection是 单列集合 的 祖宗接口,它的功能是全部单列集合都可以继承使用的。
Collection API如下:
方法名称 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中的所有元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中的元素个数 |
public Object[] toArray() | 把集合中的元素存储到数组中 |
测试代码: |
public class CollectionDemo {public static void main(String[] args) {Collection<String> collection = new ArrayList<>();// 1. 添加元素collection.add("Java");collection.add("HTML");collection.add("Mysql");collection.add("黑马");collection.add("Java");collection.add("Mysql");System.out.println(collection); // 直接打印实例化对象的名称,会默认调用toString方法,如果打印的为地址的话说明没有重写toStringSystem.out.println(collection.toString());// 2. 清空元素集合
// collection.clear();
// System.out.println(collection);// 3. 判断集合是否为空,是空返回true, 反之返回falseSystem.out.println(collection.isEmpty());// 4. 获得集合大小System.out.println(collection.size());// 5. 判断集合中是否包含某个元素System.out.println(collection.contains("java")); // falseSystem.out.println(collection.contains("Java")); // trueSystem.out.println(collection.contains("黑马")); // true// 6. 删除某个元素:如果有多个重复元素,默认删除第一个System.out.println(collection.remove("Java")); // tureSystem.out.println(collection);System.out.println(collection.remove("lushimeng")); // falseSystem.out.println(collection);// 7. 把集合转换成数组// 集合转换为数组只能是Object类型,原因在于防止数组集合中有其他类型的数据// 虽然通过<类型>进行了限制,但是可以绕过限制把其他类型的数据加入到集合中,// 所以为了安全起见都转化为Object类型的数组Object[] array = collection.toArray();System.out.println(Arrays.toString(array));// 8. 把一个集合加到另一个集合后面Collection<Integer> collection1 = new ArrayList<>();collection1.add(10);collection1.add(20);collection1.add(30);System.out.println(collection1);Collection<Integer> collection2 = new ArrayList<>();collection2.add(40);collection2.add(50);System.out.println(collection2);// addAll把collection2集合中的元素全部加入到collection中collection1.addAll(collection2);System.out.println(collection1);System.out.println(collection2);}
}
Collection集合的遍历方式
方式一:迭代器
迭代器遍历概述:遍历就是一个一个的把容器中的元素访问一遍。 迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
Collection集合获取迭代器
方法名称 | 说明 |
---|---|
Iterator<E> iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引 |
Iterator中的常用方法 | |
方法名称 | 说明 |
– | – |
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true,不存在返回false |
E next | 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出边界 |
迭代器测试案例: |
public class CollectionDemo01 {public static void main(String[] args) {Collection<Integer> collection = new ArrayList<>();collection.add(100);collection.add(200);collection.add(300);collection.add(400);System.out.println(collection);// 1. 得到当前集合的迭代器对象Iterator<Integer> it = collection.iterator(); // iterator静态方法,返回一个Iterator<Integer>对象while (it.hasNext()){int number = it.next();System.out.println(number);}}
}
问题1、迭代器的默认位置在哪里。
Iterator<E> iterator():得到迭代器对象,默认指向当前集合的索引0
问题2、迭代器如果取元素越界会出现什么问题。
会出现NoSuchElementException异常。
方式二:foreach/增强for循环
增强for循环格式:
for(元素数据类型 变量名 : 数组或者Collection集合) {//在此处使用变量即可,该变量就是元素
}
增强for循环:既可以遍历集合也可以遍历数组。
增强for循环/foreach测试案例:
public class CollectionDemo02 {public static void main(String[] args) {Collection<String> lists = new ArrayList<>();lists.add("赵敏");lists.add("校招");lists.add("潘晓");lists.add("王亮");System.out.println(lists);// foreach/增强for循环for(String str : lists){System.out.println(str);}System.out.println("-----------------------");double[] scores = {100, 98.5, 59.5};for (double score : scores) {System.out.println(score);}System.out.println(Arrays.toString(scores));}
}
问题1、增强for可以遍历哪些容器?
既可以遍历集合也可以遍历数组。
问题2、增强for的关键是记住它的遍历格式
for(元素数据类型 变量名 : 数组或者Collection集合) {//在此处使用变量即可,该变量就是元素
}
方式三:lambda表达式
Lambda表达式遍历集合:得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式。
Collection结合Lambda遍历的API
方法名称 | 说明 |
---|---|
default void forEach(Consumer<? super T> action): | 结合lambda遍历结合 |
lambda遍历结合案例: |
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;public class CollectionDemo03 {public static void main(String[] args) {Collection<String> lists = new ArrayList<>();lists.add("赵敏");lists.add("校招");lists.add("潘晓");lists.add("王亮");System.out.println(lists);// lists.forEach(new Consumer<String>() {// @Override
// public void accept(String s) {// System.out.println(s);
// }
// });// // lambda应用
// lists.forEach((String s) -> {// System.out.println(s);
// });// lambda应用
// lists.forEach( s -> System.out.println(s)
// );lists.forEach(System.out::println);}
}
Collection集合存储自定义类型的对象
需求
某影院系统需要在后台存储上述三部电影,然后依次展示出来。
分析
1:定义一个电影类,定义一个集合存储电影对象。
2:创建3个电影对象,封装相关数据,把3个对象存入到集合中去。
3:遍历集合中的3个对象,输出相关信息。
案例实现 Movie类:
public class Movie {private String name;private double score;private String actor;public Movie(){}public Movie(String name, double score, String actor){this.name = name;this.score = score;this.actor = actor;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}public String getActor() {return actor;}public void setActor(String actor) {this.actor = actor;}@Overridepublic String toString() { // 返回值类型为String类型, 在Movie类中重写toString,要不然会打印地址而不是内容return "Movie{" +"name='" + name + '\'' +", score=" + score +", actor='" + actor + '\'' +'}';}
}
案例实现 Test测试类:
import java.util.*;
import java.util.function.Consumer;public class TestDemo {public static void main(String[] args) {Collection<Movie> movieCollection = new ArrayList<>();movieCollection.add(new Movie("《你好, 李焕英》", 9.5 , "贾玲"));movieCollection.add(new Movie("《唐人街探案》", 8.5, "王宝强"));movieCollection.add(new Movie("《刺杀小说家》", 8.6 , "雷佳音"));System.out.println(movieCollection); // 默认调用toString方法,// 1. 迭代器访问集合Iterator<Movie> it = movieCollection.iterator();while (it.hasNext()){Movie movie = it.next();System.out.println(movie.toString());}System.out.println("--------------------------------------------");// 2. foreach/增强for循环访问集合for(Movie movie : movieCollection){System.out.println("电影名称:" + movie.getName() + ", 评分:" + movie.getScore() + ", 演员:" + movie.getActor());}System.out.println("--------------------------------------------");// 3. lambda访问集合
// movieCollection.forEach(new Consumer<Movie>() {// @Override
// public void accept(Movie movie) {// System.out.println("电影名称:" + movie.getName() +
// ", 评分:" + movie.getScore() +
// ", 演员:" + movie.getActor());
//
// }
// });// movieCollection.forEach((Movie movie) -> {// System.out.println("电影名称:" + movie.getName() +
// ", 评分:" + movie.getScore() +
// ", 演员:" + movie.getActor());
//
// });// movieCollection.forEach(movie -> System.out.println("电影名称:" + movie.getName() + ", 评分:" + movie.getScore() + ", 演员:" + movie.getActor()));// movieCollection.forEach(System.out::println); // Movie类中要重写toString方法,要不然输出的为地址}
}
List系列集合
List集合特点、特有API
List系列集合特点:ArrayList、LinekdList :有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
List集合特有方法:List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
方法测试案例:
public class ListDemo01 {public static void main(String[] args) {// 1. 创建一个ArrayList集合对象// List: 有序,可重复,有索引List<String> list = new ArrayList<>();list.add("Java");list.add("Java");list.add("HTML");list.add("HTML");list.add("Mysql");list.add("Mysql");System.out.println(list);// 2. 在某个索引位置插入元素list.add(2, "黑马");System.out.println(list);// 3. 根据索引删除元素,返回被删除元素System.out.println(list.remove(1));System.out.println(list);// foreach / 增强for循环for(String str : list){System.out.println(str);}// 5. 修改索引位置处的元素System.out.println(list.set(1, "lushimeng"));System.out.println(list);}
}
问题1、List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引。
问题2、List的实现类的底层原理
ArrayList底层是 基于数组 实现的,根据查询元素快,增删相对慢。
LinkedList底层 基于双链表 实现的,查询元素慢,增删首尾元素是非常快的。
List集合的遍历方式小结
List集合的遍历方式有几种?
1、迭代器 ;
2、增强for循环;
3、Lambda表达式;
4、for循环(因为List集合存在索引)
测试案例:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class ListDemo02 {public static void main(String[] args) {// 1. 创建一个ArrayList集合对象// List: 有序,可重复,有索引List<String> list = new ArrayList<>();list.add("Java");list.add("Java");list.add("HTML");list.add("HTML");list.add("Mysql");list.add("Mysql");System.out.println(list);// 4. 根据索引获取元素// 4.1 for循环遍历方式for(int i = 0; i < list.size(); i++){System.out.println(list.get(i));}System.out.println("------------------------------");// 4.2 迭代器iterator遍历方式Iterator<String> it = list.iterator();while (it.hasNext()){String str = it.next();System.out.println(str);}System.out.println("------------------------------");// 4.3 foreach / 增强for循环for(String str : list){System.out.println(str);}System.out.println("------------------------------");// 4.4 JDK1.8之后开始有lambda表达式list.forEach(s -> System.out.println(s));System.out.println("------------------------------");list.forEach(System.out::println);}
}
ArrayList集合的底层原理
ArrayList集合底层原理
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。 第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
LinkedList集合的底层原理
LinkedList的特点:底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API,可以用其做入栈,出栈,队列等操作。
LinkedList集合的特有功能:
测试案例:
public class ListDemo03 {public static void main(String[] args) {// LinkedList可以完成队列结构和栈结构(双链接)// 1. 做一个队列LinkedList<String> queue = new LinkedList<>();// 1. 入队queue.addLast("1");queue.addLast("2");queue.addLast("3");queue.addLast("4");queue.addLast("5");System.out.println(queue); // [1, 2, 3, 4, 5]// 出队System.out.println(queue.getFirst());System.out.println(queue.removeFirst());System.out.println(queue); // [2, 3, 4, 5]// 1. 做一个栈LinkedList<String> stack = new LinkedList<>();// 入栈stack.push("第1颗子弹");stack.push("第2颗子弹");stack.push("第3颗子弹");stack.push("第4颗子弹");System.out.println(stack); // [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]// 出栈System.out.println(stack.removeFirst()); // 第4颗子弹System.out.println(stack.pop()); // 第3颗子弹System.out.println(stack); // [第2颗子弹, 第1颗子弹]System.out.println("------------------------------------");LinkedList<Integer> linkedList = new LinkedList<>();linkedList.addLast(1);linkedList.addLast(2);linkedList.addLast(3);linkedList.addLast(4);linkedList.addLast(5);linkedList.addLast(6);linkedList.addLast(7);for (int i = 0; i < linkedList.size(); i++) {System.out.print(linkedList.get(i) + " ");}System.out.println();System.out.println("------------------------------------");// 迭代器Iterator<Integer> it = linkedList.iterator();while (it.hasNext()){System.out.println(it.next());}System.out.println("------------------------------------");// foreach/增强for循环for(Integer integer : linkedList){System.out.println(integer);}System.out.println("------------------------------------");// jdk 1.8之后lambda表达式遍历集合方式linkedList.forEach(s -> System.out.println(s));}
}
Set系列集合
Set系列集系概述
Set系列集合特点
1、无序:存取顺序不一致
2、不重复:可以去除重复
3、无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点
1、HashSet : 无序、不重复、无索引。
2、LinkedHashSet:有序、不重复、无索引。
3、TreeSet:排序、不重复、无索引。
Set集合的功能上基本上与Collection的API一致。
测试代码:
public class SetDemo1 {public static void main(String[] args) {// Set系列集合的特点:HashSet LinkedHashSet TreeSet
// Set<String> set = new HashSet<>(); // 无序 不重复 无索引 [Java, A, Mysql, SpringBoot]
// Set<String> set = new LinkedHashSet<>(); // 有序 不重复 无索引 [Java, Mysql, SpringBoot, A]Set<String> set = new TreeSet<>(); // 升序 不重复 无索引 [Java, Mysql, SpringBoot]set.add("Java");set.add("Java");set.add("Mysql");set.add("Mysql");set.add("SpringBoot");set.add("SpringBoot");set.add("A");System.out.println(set); // 默认调用toString方法,集合都重写了Object类中的toString方法}
}
HashSet元素无序的底层原理:哈希表
HashSet底层原理
HashSet集合底层采取 哈希表 存储的数据。
哈希表是一种对于增删改查数据性能都较好的结构。
哈希表的组成
JDK8之前的,底层使用 数组 + 链表 组成
JDK8开始后,底层采用 数组 + 链表 + 红黑树 组成。
哈希值:是JDK根据对象的地址,按照某种规则算出来的int类型的数值,Object类的API public int hashCode():返回对象的哈希值
对象的哈希值特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。
HashSet1.7版本原理解析:数组 + 链表 +(结合哈希算法)
1、创建一个默认长度16的数组,数组名table
2、根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
3、判断当前位置是否为null,如果是null直接存入
4、如果位置不为null,表示有元素,则调用equals方法比较
5、如果一样,则不存,如果不一样,则存入数组JDK 7新元素占老元素位置,指向老元素JDK 8中新元素挂在老元素下面`
JDK1.8版本开始HashSet原理解析
底层结构:哈希表(数组、链表、红黑树的结合体)
当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。
问题1:Set集合的底层原理是什么样的?
JDK8之前的,哈希表:底层使用数组+链表组成
JDK8开始后,哈希表:底层采用数组+链表+红黑树组成。
问题2… 哈希表的详细流程
1、 创建一个默认长度16,默认加载因为0.75的数组,数组名table
2、 根据元素的哈希值跟数组的长度计算出应存入的位置
3、判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素, 则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组。
4、当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
问题提出:你可以发现下图中new了两个字符串对象str1,str2两个字符的内容相同,按照上面对象的哈希值特点知:不同对象的哈希值是不同的。但是下面的实验结果却显示相同,这个要怎么理解呢?
问题解答:经过调研发现与equals和hashCode方法重写有关,我们知道任何类都继承Object类,Object类中有equals方法,方法的作用是比较两个对象的地址是否一样。而我们在String类里面重写了equlas方法,用来比较字符串内容是否一致。
主要是Object.hashCode的通用约定:
a. 在java应用程序运行时,无论何时多次调用同一个对象时的hsahCode()方法,这个对象的hashCode()方法的返回值必须是相同的一个int值.
b. 如果两个对象equals()返回值为true,则他们的hashCode()也必须返回相同的int值.
c. 如果两个对象equals()返回值为false,则他们的hashCode()返回值也必须不同.
String s1 = new String("1111");String s2 = new String("1111");s1.hashCode();s2.hashCode();System.out.println(s1.equals(s2));
s1和s2对象所指的内存地址是不一样的,一个对象的hashcode是内存地址进行hash运算而得,如果string没有重写hashcode,那么s1和s2的hashcode 也有可能是不一样的。如果用Obejct的equals方法,那么比较的手机两个对象在堆内存的地址,那么结果会是是false,但是在业务系统中我们需要的是对象属性是否一致,所以重写了equals方法。string的底层数据结构是char数组,所以重写equals的逻辑就是两个对象的char数组循环比较值。那么equals重写了,但是如果hashcode没有重写那就违反了Object.hashCode的通用约定,所以hashCode必须也重写。可以从源码看出,两个值相等的String 的hashCode一定相等。
参考连接:https://www.cnblogs.com/zhengyixin/p/14891400.html
HashSet元素去重复的底层原理
1、创建一个默认长度16的数组,数组名table
2、根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
3、判断当前位置是否为null,如果是null直接存入
4、如果位置不为null,表示有元素,则调用equals方法比较
4、如果一样,则不存,如果不一样,则存入数组,
结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法
案例需求:
创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合,要求:学生对象的成员变量值相同,我们就认为是同一个对象分析:
1、定义学生类,创建HashSet集合对象, 创建学生对象
2、把学生添加到集合
3、在学生类中重写两个方法,hashCode()和equals(),自动生成即可
4、遍历集合(增强for)
Student类:
import java.util.Objects;public class Student {private String name;private int age;private char sex;public Student() {}public Student(String name, int age, char sex) {this.name = name;this.age = age;this.sex = sex;}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 char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}/*** 重写equals方法,判断内容是否想等* @param o* @return*/@Overridepublic 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 && sex == student.sex && Objects.equals(name, student.name);}/*** 重写hashCode方法,把相容内容的对象只存储一个。* @return*/@Overridepublic int hashCode() {return Objects.hash(name, age, sex);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +'}';}
}
StudentTest测试类:
/*** 在自定义类进行比较的时候,如果不设定比较规则会报错:ClassCastException*/
public class StudentTest {public static void main(String[] args) {// 方法二:集合自定义Comparator比较器对象,重写比较规则。// Comparator前面有@FunctionalInterface,可以使用lambda负责
// Set<Student> set = new TreeSet<>(new Comparator<Student>() {// @Override
// public int compare(Student o1, Student o2) {// return Double.compare(o1.getSum(), o2.getSum()); // 升序
return Double.compare(o2.getSum(), o1.getSum()); // 降序
// }
// });
// Set<Student> set = new TreeSet<>( (o1,o2) -> Double.compare(o1.getSum(), o2.getSum())); // 升序Set<Student> set = new TreeSet<>( (o1,o2) -> Double.compare(o2.getSum(), o1.getSum())); // 降序set.add(new Student("卢一", 100, 100, 100));set.add(new Student("赵二", 99, 93, 34));set.add(new Student("王三", 99, 99, 99));set.add(new Student("宋四", 87, 78, 78));System.out.println(set.toString());Student student1 = null; // 接收变量// 迭代器访问集合Iterator<Student> it = set.iterator(); // 获取迭代器对象while (it.hasNext()){ // 访问当前指针所指向的位置,是否有值student1 = it.next(); // 把内容取出来赋给student,然后指针向后移动一位System.out.println(student1.getName() + "\t" +student1.getChinese() + "\t" +student1.getMath() + "\t" +student1.getEnglish() + "\t" +student1.getSum());}System.out.println("--------------------------------------------------------");// 增强for循环访问集合for(Student stu : set){System.out.println(stu.getName() + "\t" +stu.getChinese() + "\t" +stu.getMath() + "\t" +stu.getEnglish() + "\t" +stu.getSum());}System.out.println("--------------------------------------------------------");// lambda访问集合set.forEach(student -> System.out.println(student.getName() + "\t" + student.getChinese() + "\t" +student.getMath() + "\t" +student.getEnglish() + "\t" +student.getSum()));}
}
代码截图:
实现类:LinkedHashSet
LinkedHashSet集合概述和特点
特点:有序(元素添加的顺序,而不是排序)、不重复、无索引。这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然 哈希表,只是每个元素又额外的多了一个 双链表的机制 记录存储的顺序
实现类:TreeSet
TreeSet集合概述和特点
不重复、无索引、可排序
可排序:按照元素的大小 默认 升序(有小到大) 排序。
TreeSet集合底层是 基于红黑树 的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
测试案例:
import java.util.*;
// 底层实现方式为链表 + 红黑树
public class SetDemo5 {public static void main(String[] args) {Set<Integer> sets = new TreeSet<>(); // 可排序 不重复 无索引sets.add(23);sets.add(12);sets.add(45);sets.add(9);System.out.println(sets); // [9, 12, 23, 45]Set<String> set = new TreeSet<>(); // 有序 无重复 无索引set.add("Java");set.add("Java");set.add("angela");set.add("黑马");set.add("Java");set.add("About");set.add("UI");System.out.println(set); // [About, Java, UI, angela, 黑马]
TreeSet集合默认的规则
对于数值类型:Integer , Double,官方默认按照大小进行 升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet无法直接排序, 要使用自定义的排序规则。
结论:想要使用TreeSet存储自定义类型,需要制定排序规则
自定义排序规则:TreeSet集合存储对象的的时候有2种方式可以设计自定义比较规则
方式一: 让自定义的类(如学生类) 实现Comparable接口 重写 里面的 compareTo方法 来定制比较规则。
方式二:TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
两种方式中,关于返回值的规则:
如果认为第一个元素大于第二个元素返回正整数即可。
如果认为第一个元素小于第二个元素返回负整数即可。
如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
注意:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。
TreeSet对象排序练习题
需求:键盘录入3个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台分析
1、定义学生类
2、创建TreeSet集合对象,通过比较器排序进行排序
3、创建学生对象
4、把学生对象添加到集合
5、遍历集合
Student类
public class Student implements Comparable<Student>{private String name;private double chinese;private double math;private double english;private double sum;public Student() {}public Student(String name, double chinese, double math, double english) {// this.sum = chinese + math + english;this.name = name;this.chinese = chinese;this.math = math;this.english = english;// 必须放在chinese, math, english都已经赋值后的后面才可以,放在其前面,变量还没有被赋值,则sum计算结果为0this.SUM();}public void SUM(){this.sum = this.chinese + this.math + this.english;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getChinese() {return chinese;}public void setChinese(double chinese) {this.chinese = chinese;}public double getMath() {return math;}public void setMath(double math) {this.math = math;}public double getEnglish() {return english;}public void setEnglish(double english) {this.english = english;}public double getSum() {return sum;}public void setSum(double sum) {this.sum = sum;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", chinese=" + chinese +", math=" + math +", english=" + english +", sum=" + sum +'}';}/*** 类自定义比较规则,实现 Comparable接口 重写 里面的 compareTo方法 来定制比较规则* o2.compareTo(o1);* @param o* @return*/@Overridepublic int compareTo(Student o) {// return this.sum >= o.sum ? 1 : -1; // 按照升序排列return o.sum >= this.sum ? 1 : -1; // 按照降序排列}
}
StudentTest类:
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Consumer;/*** 在自定义类进行比较的时候,如果不设定比较规则会报错:ClassCastException*/public class StudentTest {public static void main(String[] args) {// 方法二:集合自定义Comparator比较器对象,重写比较规则。// Comparator前面有@FunctionalInterface,可以使用lambda负责
// Set<Student> set = new TreeSet<>(new Comparator<Student>() {// @Override
// public int compare(Student o1, Student o2) {// return Double.compare(o1.getSum(), o2.getSum()); // 升序
return Double.compare(o2.getSum(), o1.getSum()); // 降序
// }
// });
// Set<Student> set = new TreeSet<>( (o1,o2) -> Double.compare(o1.getSum(), o2.getSum())); // 升序Set<Student> set = new TreeSet<>( (o1,o2) -> Double.compare(o2.getSum(), o1.getSum())); // 降序set.add(new Student("卢一", 100, 100, 100));set.add(new Student("赵二", 99, 93, 34));set.add(new Student("王三", 99, 99, 99));set.add(new Student("宋四", 87, 78, 78));System.out.println(set.toString());Student student1 = null; // 接收变量// 迭代器访问集合Iterator<Student> it = set.iterator(); // 获取迭代器对象while (it.hasNext()){ // 访问当前指针所指向的位置,是否有值student1 = it.next(); // 把内容取出来赋给student,然后指针向后移动一位System.out.println(student1.getName() + "\t" +student1.getChinese() + "\t" +student1.getMath() + "\t" +student1.getEnglish() + "\t" +student1.getSum());}System.out.println("--------------------------------------------------------");// 增强for循环访问集合for(Student stu : set){System.out.println(stu.getName() + "\t" +stu.getChinese() + "\t" +stu.getMath() + "\t" +stu.getEnglish() + "\t" +stu.getSum());}System.out.println("--------------------------------------------------------");// lambda访问集合set.forEach(student -> System.out.println(student.getName() + "\t" + student.getChinese() + "\t" +student.getMath() + "\t" +student.getEnglish() + "\t" +student.getSum()));}
}
问题1:如果希望元素可以重复,又有索引,索引查询要快?
用ArrayList集合,基于数组的。(用的最多)
问题2: 如果希望元素可以重复,又有索引,增删首尾操作快?
用LinkedList集合,基于链表的。
问题3:如果希望增删改查都快,但是元素不重复、无序、无索引。
用HashSet集合,基于哈希表的。
问题4:如果希望增删改查都快,但是元素不重复、有序、无索引。
用LinkedHashSet集合,基于哈希表和双链表。
问题5:如果要对对象进行排序。
用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。
Java中的Collection集合以及Collection集合实现类实例相关推荐
- java中list,set,map集合的区别,及面试要点
Map集合:链接: Map集合的五种遍历方式及Treemap方法 Set集合:链接: Java中遍历Set集合的三种方法 TreeSet集合:链接: Java深入了解TreeSet,和迭代器遍历方法 ...
- Java中 方法引用、Stream流、及方法实例 D190401
Java中 方法引用.Stream流.及方法实例 D190401 01.第三章:方法引用_什么是方法引用 1).什么是"方法引用":指引用现有的方法代替Lambda表达式--当我们 ...
- java中使用jxl导出excel表格的工具类(全网唯一亲测可用,在原来基础上扩展)
java中后台导出excel的话,有两种方案,一是使用poi(不过由于是windows版本的,存在不兼容,但功能更多,更强大),而是使用jxl(纯java编写,不过兼容,简单一些),可以设置输出的ex ...
- Java中的数组,列表,集合,映射,元组,记录文字
有时,当我对JavaScript的强大功能和表现力感到兴奋时,我发现自己错过了Java世界中的一两个功能. 除了lambda表达式/闭包或任何您想称为"匿名函数"的东西之外,它还对 ...
- JAVA中如何交换两个List集合的顺序呢?
在JAVA中我们该如何交换两个List集合的顺序呢? /*** 交换两个集合的顺序* * @param a a集合* @param b b集合*/public static void swapList ...
- java中list元素排序_java list集合元素根据某些字段排序
一.jdk1.6的环境下 新建ComparatorSort类,并实现Comparator接口,重写compare方法 降序排序:o1的元素>o2的元素,并返回-1:o1的元素小于o2的元素,并返 ...
- 六、Java中常用的API(通过包进行分类)————File类、IO流
一.util工具包下的补充类 介绍io包之前,先补充介绍几个常用的工具类 1.Scanner类 什么是Scanner类 一个可以解析基本类型和字符串的简单文本扫描器. 例如,以下代码使用户能够从 Sy ...
- java中的switch用法,循环,方法,数组以及类
选择结构语句之switch: switch语句在开发过程中的使用仅次于if语句的使用. switch语句: switch语句格式: switch(表达式) { case 值1: 语句体1; break ...
- Java中怎样将Json字符串转换成实体类
场景 在Java中调用接口获取Json数据后,怎样转换成对应的实体类进行接受与存储. 实现 打开浏览器输入在线Json格式化,这里推荐使用: http://www.bejson.com/ 选择Json ...
- java中商业数据计算时用到的类BigDecimal和DecimalFormat
1.引言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确 ...
最新文章
- spark mysql 驱动_spark读取mysql数据库的驱动问题
- python【蓝桥杯vip练习题库】ADV-236林丹大战李宗伟
- Classical Inheritance in JavaScript
- java 迪杰斯特拉_Java 实现Dikstra迪杰斯特拉算法 关于单源顶点最短路径问题的求解...
- Github、Jekyll 搭建及优化静态博客方法指南
- 单片机复位电路电容一定用电解电容_什么叫51单片机最小系统
- [导入]数据库物理模型设计的其他模式之继承模式
- php strpos与strrpos,PHP开发之 strpos stripos strrpos strripos的区别
- TextDetection文本检测数据集汇总
- R语言转换并保存json文件--使用jsonlite包
- 理想传输线终端短路开路和接纯电抗的沿线电压电流分布
- window安装python3后怎么用pyspark_pyspark:连接spark集群Windows环境搭建
- android recyclerview item自适应高度_web前端学习:高度自适应知识点
- Mac 自定义用户级别Applications, 安装应用到Dashboard
- h5 移动端电子签名
- IDEA:Windows 下载安装 IDEA 详细教程
- 计算机系统中引入多道程序设计的目的在于,引入多道程序的目的在于什么
- 程序员述职报告范文_程序员个人年度工作总结范文
- 2020年鼠年正月十二 淡然面对
- 这些独家记忆串成我们的2022