集合
一些数据结构的统称集合 ,包含List,Set,Map
List(列表)
List就是列表,用来存储一组数据的数据结构
List可变长的数据结构,可以知道List里面存有多少个数据
List里面可以保存不同类型的数据
List是有序列表,数据可以重复
ArrayList
ArrayList是采用数组方式实现的
读取和遍历比较快,插入和删除数据的时候比较慢
-
List的基本使用
add( )
get( )
size( )
//new一个ArrayList列表
List list = new ArrayList();//默认长度是10,自动扩展
list.add(5);
list.add("abc");
list.add(null);
list.add(5);
//size是返回列表的实际的个数
System.out.println(list.size());
//获取指定下标的数据
Object o = list.get(1);
System.out.println(o);
Integer m = (Integer) list.get(3);
System.out.println(m);
String n = (String)list.get(2);
System.out.println(n);
//for循环遍历list
System.out.println("=====遍历list========");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//foreach循环遍历list
System.out.println("====foreach==遍历list=====");
for(Object obj:list){
System.out.println(obj);
}
- List的扩展
//list.add(x,x)括号里面两个数字,逗号隔开
//在指定位置插入数据,插入的数据就在指定的下标,原来的数据往后移一位
list.add(2,3.15);
//list.set()替换指定下标位置的元素
list.set(4,true);
//删除指定下标的元素
list.remove(3);
//判断是否包含某个元素,如果包含返回true
System.out.println("是否包含abc:" + list.contains("abc"));
//返回指定元素在列表中的序号:没有找到返回-1
System.out.println(list.indexOf("abc"));
System.out.println(list.lastIndexOf("abc"));
//截取子列表,左闭右开区间
List listSub = list.subList(1,3);
System.out.println(listSub);
//清除列表所有数据
list.clear();
//List列表转数组
Object[]array = list.toArray();
System.out.println("数组长度:" + array.length);
List list1 = Arrays.asList();
System.out.println();
//判断列表是否是空的(没有元素)是空返回true不是空返回false
System.out.println(list.isEmpty());
LInkedList
采用双向链表实现的,插入和删除速度快,查询和遍历速度慢
- 插入和删除速度快
- 查找和遍历速度慢
- 链里面的数据是保存的地址
- 每一个节点除了自身数据的地址外,还保存上一个和下一个节点的地址
LinkedList的一些特殊的方法
public static void main(String[] args) {
//创建一个LinkedList对象
LinkedList list = new LinkedList();
list.add(10);
//在列表的最前面插入一个元素
list.addFirst(5);
//在列表的最后面插入一个元素
list.addLast(9);
System.out.println("=======遍历列表========");
for(Object obj:list){
System.out.println(obj);
}
//获取第一个元素
System.out.println("第一个元素:" + list.getFirst());
//获取最后一个元素
System.out.println("最后一个元素:" + list.getLast());
//删除第一个节点
list.removeFirst();
//删除最后一个节点
list.removeLast();
list.add(50);
list.add(10);
//出栈:弹出第一个元素(节点),会从列表中删除
System.out.println("弹出元素:" + list.pop());
//入栈(压栈):在最前面插入一个元素(节点)
list.push(23);
System.out.println("=======遍历列表========");
for(Object obj:list){
System.out.println(obj);
}
}
泛型
因为List可以存各种类型的数据,但是取出来都是Objest,所以需要进行强制转换
泛型就是用来解决这个问题的,在创建List对象时指定存储的类型,在add和get自动就采用泛型指定的类型
定义方式:ListlistStr = new LinkedList<>();
泛型使用
//泛型
//定义泛型:定义变量的时候,类型后面跟上尖括号,尖括号写上那个数据类型
//new 对象的时候,类后面也要有尖括号,尖括号里不写类型,默认跟定义变量泛型一致
//泛型只能使用引用类型,不能使用基本类型
List<String>listStr = new LinkedList<>();
//一旦指定类型后,相关方法的参数,返回类型都跟泛型的类型一样
listStr.add("aaaaaaaaa");
listStr.add("bbbbbbbbb");
String s = listStr.get(0);
System.out.println(s);
Set
无序集合,元素不重复
没有索引(序号),不能通过下标访问
哈希表(hash table)
哈希表,散列表,是一种高效的数据结构
要保存的数据称为值,根据hash算法给每一个值算出hash code
保存数据用hash code 跟值一一对应
如下图所示,左边是数组方式保存数据,右边就是哈希表方式保存,中间是一个模拟的hash算法
hash table保存数据的原理
根据hash code从表里面找是否存在:
- 不存在,直接添加
- 存在,再去判断equals是否相等
- false,直接添加
- true,说明两个值一样,不添加
Hash code结论:
- 两个值的hash code相同,它们的equals不一定相同
String str1="通话";
String str2="重地";
System.out.println(str1.hashCode());//1179395
System.out.println(str2.hashCode());//1179395
System.out.println(str1.equals(str2));//false
- 两个值的equals相同,hash code不一定相同
//两个对象的equals相同,hash code不一定相同
Student student1 = new Student(100,"张三");
Student student2 = new Student(100,"李四");
System.out.println(student1.hashCode() == student2.hashCode());
}
static class Student {
private int num;
private String Name;
public Student(int num, String name) {
this.num = num;
Name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return num == student.num && Name.equals(student.Name);
}
@Override
public int hashCode() {
return Objects.hash(num, Name);
}
}
- 两个值的hash code 相同,equls也相同,这两个值一定相同
HashSet
采用hash表实现
元素不重复,无序的,允许存null
HashSet的常用方法:
public static void main(String[] args) {
//创建HashSet,默认长度16,加载因子为0.75
HashSet <String>set = new HashSet<String>();
//往set添加元素
set.add("Java");
set.add("Html");
set.add("SQL");
set.add("Java");//内容重复,在set里里面只有一个
//返回set的长度
System.out.println(set.size());
//读取set的元素:遍历
for (String str:set) {
System.out.println(str);
}
//查找是否包含某个对象(对象类要重写hash code 和 equals)
System.out.println(set.contains("SQL"));
//删除set里面的元素
set.remove("Java");
//清空set
set.clear();
//判断set是否是空的
System.out.println(set.isEmpty());
}
练习
给出一个数组,把数组中重复的元素去掉
{1,30,25,40,18,30,22,40}
//数组去除重复数据
int[] arr1 = {1,30,25,40,18,30,22,40};
HashSet <Integer> set1 = new HashSet<Integer>();
for (int a:arr1) {
set1.add(a);
}
int[] arr2 = new int[set1.size()];
int i = 0;
for (int a:set1) {
arr2[i] = a;
i++;
}
System.out.println(Arrays.toString(arr2));
二叉树
-
满二叉树
-
完全二叉树
最底层的分支可以不是完全的,但是必须要先保证左边的分支全部填满
- 二叉查找树
-
平衡二叉树
左右子树的高度差小于等于1,并且左右子树分别都是平衡的二叉树
-
红黑树
它是一种自平衡的二叉查找树
特征:
-
节点只有红色和黑色
-
根节点是黑色
-
叶子节点是黑色,叶子节点村NIL
-
一个节点为红色,则他的两个子节点必须为黑色(从根节点到叶子节点的所有路径不可能存在两个连续的红色节点)
-
每一个节点到叶子节点的所有路径都包含相同数目的黑色节点
-
TreeSet
TreeSet的特性
-
TreeSet采用红黑树数据结构实现
-
不能添加null
-
不能添加重复数据
-
添加的元素会被排序,遍历出来的是按顺序排列的
-
TreeSet的元素必须要实现Comparable接口(如果没有实现Compareable接口,运行时抛出类型转换异常)
常用的方法
public static void main(String[] args) {
//创建set
TreeSet<Integer> set = new TreeSet<>();
set.add(20);
set.add(25);
set.add(8);
set.add(16);
set.add(10);
//返回元素的个数
System.out.println("元素的个数:" + set.size());
//返回排序后的第一个元素
System.out.println("first:" + set.first());
//返回排序后的最后一个元素
System.out.println("last:" + set.last());
//返回比指定元素大的最小元素 ceiling:天花板
System.out.println("ceiling:" + set.ceiling(18));
//返回比指定元素小的最大元素 floor:地板
System.out.println("" + set.floor(18));
//遍历是按元素排序显示
for (Integer i:set) {
System.out.println(i + " ");
}
//删除指定的对象
set.remove(20);
//清空所有的元素
set.clear();
//判断是否是空
System.out.println("判断是否是空:" + set.isEmpty());//true
TreeSet存放自定义的类型
-
定义Egg类
必须实现接口Compareable
实现CompareableTo()方法,当前对象比参数大返回1,小于返回-1,等于返回0
///类要放进TreeSet(排序),必须要实现Comparable接口,泛型和这个类一样
public class Egg implements Comparable<Egg> {
private float weight;
public Egg(float weight) {
this.weight = weight;
}
public Egg() {
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Egg egg = (Egg) o;
return Float.compare(egg.weight, weight) == 0;
}
@Override
public int hashCode() {
return Objects.hash(weight);
}
@Override
public String toString() {
return "Egg{" +
"weight=" + weight +
'}';
}
/**
* 实现compareTo方法
* o 跟它比较的另一个对象
* 1 当前对象大于参数对象,返回 1 (升序排列,如果要降序就返回 -1)
* -1 当前对象小于参数对象,返回 -1
* 0 当前对象跟参数对象相等,返回 0
*/
@Override
public int compareTo(Egg o) {
if(this.weight > o.weight){
return 1;
}else if(this.weight < o.weight){
return -1;
}else{
return 0;
}
}
}
-
测试代码
TreeSet里面的对象按升序排列
// TreeSet存放自定义对象
TreeSet<Egg> eggs = new TreeSet<>();
eggs.add(new Egg(50.0f));
eggs.add(new Egg(45.5f));
eggs.add(new Egg(78.5f));
eggs.add(new Egg(41f));
for (Egg egg:eggs) {
System.out.println(egg);
}
练习
创建运动员(SportsMan)对象,有个成绩(score)和姓名(name)属性
按照的得分从高到底进行排序,打印运动员的姓名和成绩
-
SportsMan类
public class SportsMan implements Comparable<SportsMan>{ private String name; private int score; public SportsMan(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SportsMan sportsMan = (SportsMan) o; return score == sportsMan.score && Objects.equals(name, sportsMan.name); } @Override public int hashCode() { return Objects.hash(name, score); } @Override public String toString() { return "SportsMan{" + "姓名:'" + name + '\'' + ", 成绩:" + score + '}'; } @Override public int compareTo(SportsMan o) { if(this.score > o.score){ return -1;//升序 }else if(this.score < o.score ){ return 1;//降序 }else{ return 1; } } }
-
测试类 SportsTest
public class SportsTest { public static void main(String[] args) { TreeSet<SportsMan> sportsman = new TreeSet<>(); sportsman.add(new SportsMan("张三",60)); sportsman.add(new SportsMan("李四",85)); sportsman.add(new SportsMan("王五",78)); sportsman.add(new SportsMan("赵六",90)); sportsman.add(new SportsMan("刘七", for (SportsMan s: sportsman) { System.out.println(s); } } }
Map
-
保存键值对数据(key - value)
如 :yyds -> 永远的神
emo -> 抑郁了
-
key不能重复,value可以重复
-
key和value都可以为null
HashMap
采用数组 + 链表 + 红黑树 的数据结构来实现的
先根据hash code 保存数组中
如果出现hash碰撞,用链表
链表长度超过8,改用红黑树
基本方法的使用
public static void main(String[] args) {
//默认长度是16,加载因子0.75
HashMap<String,String> map = new HashMap<>();
//往map插入数据,第一个参数是key,第二个参数是value
map.put("yyds","永远的神");
map.put("emo","抑郁了");
map.put("u1s1","有一说一");
//根据key查询对象value
System.out.println(map.get("yyds"));
//判断map中是否存在某个key
System.out.println("是否存在key:" + map.containsKey("yyds"));
//判断map中是否存在某个value
System.out.println("是否存在value:" + map.containsValue("有一说一"));
//返回map元素长度
System.out.println(map.size());
//遍历map
System.out.println("==========遍历value==========");
Collection<String> values = map.values();
for (String str: values) {
System.out.println(str);
}
System.out.println("==========遍历key==========");
Collection<String> key = map.keySet();//以set数据类型返回所有的key
for (String str: key) {
System.out.println(str);
}
//同时遍历key和value
System.out.println("=======遍历entry=========");
Set<Map.Entry<String, String>> entries = map.entrySet();//Map.Entry:内部的接口或者类
for (Map.Entry<String,String> entry:entries) {
System.out.println(entry.getKey() + "," + entry.getValue());
}
//删除key为指定的元素
map.remove("yyds");
//清除所有元素
map.clear();
//判断map是否为空
System.out.println(map.isEmpty());
}
迭代
map不能做迭代遍历
list做迭代遍历
List<Integer> list = new ArrayList<>();
list.add(9);
list.add(6);
list.add(3);
list.add(45);
list.add(42);
list.add(13);
//返回迭代器,迭代器里面存着数据
Iterator<Integer> iterator = list.iterator();
//hasNext()方法判断里面还有没有数据
while (iterator.hasNext()){
//next()从迭代器中取出数据,取一个少一个
Integer integer = iterator.next();
System.out.println(integer);
}
set做迭代遍历
/*set做迭代遍历 */
Set<Integer> set = new HashSet<>();
set.add(52);
set.add(32);
set.add(20);
set.add(12);
set.add(5);
set.add(45);
Iterator<Integer> iterator1 = set.iterator();
while(iterator1.hasNext()){
Integer integer= iterator1.next();
System.out.println(integer);
}
Collections集合工具类
-
面试题:Collection跟Collections的区别
Collection:是一个接口,它是List和Set的父接口
Collections:是一个处理集合类型数据的工具类
-
Collection 的常用方法
List<Integer> list = new ArrayList<>();
list.add(56);
list.add(50);
list.add(20);
list.add(30);
list.add(10);
//随机打乱list的顺序
Collections.shuffle(list);
for (Integer i: list) {
System.out.print(i + " ");
}
//让list逆序排列
System.out.println("=====逆序排列=====");
Collections.reverse(list);
for (Integer i: list) {
System.out.print(i + " ");
}
//让list排序
//如果要排序,list里面的类型必须要实现Comparable接口(类似TreeSet里面的对象)
Collections.sort(list);
System.out.println("========排序========");
for (Integer i :list){
System.out.print(i + " ");
}
//交换指定位置的元素
Collections.swap(list,1,3);
System.out.println("====交换=====");
for (Integer i:list) {
System.out.print(i + " ");
}
//返回集合中的一个元素
System.out.println("====最大====");
Integer max = Collections.max(list);
System.out.println(max);
System.out.println("======最小=====");
Integer min = Collections.min(list);
System.out.println(min);