集合概述
java中存储对象数据的一种容器,集合只能存储引用类型的数据。用泛型来规定需要操作元素的数据类型,可以在编译阶段约束集合只能操作某种数据类型。集合分为两个家族 MAP和Collection
1.集合特点
大小不固定(自动扩容,不用定义长度),启动后可以动态变化,类型也可以选择不固定。
集合非常适合做元素的增删操作。 因为数组增删操作比较慢,因此在进行频繁的增删业务的时候就可以选择集合来存储数据。
Collection集合的体系特点
Collection集合特点
1.List系列集合特点:
添加的元素都是有序,可重复,有索引的。
ArrayList,LinkedList: 有序, 可重复, 有索引
2.Set系列集合特点:
添加的元素都是无序的,不可重复,没有索引
HashSet:: 无序,不重复,无索引。 LinkedHashSet:有序,不重复,无索引
TreeSet: 按照大小默认升序排序 不重复 无索引。
代码块:
package com.itheima.d1_Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class CollectionDemo1 {
public static void main(String[] args) {
// 有序 可重复 有索引
Collection list = new ArrayList<>();
list.add("Java");
list.add("Java");
list.add("Myatis");
list.add(23);
System.out.println(list);
//无序 不重复 无索引
Collection list1 = new HashSet();
list1.add("Java");
list1.add("Java1");
list1.add("Myatis");
list1.add("Myatis1");
list1.add("Myatis2");
list1.add(23);
System.out.println(list1);
//集合对于泛型的支持
Collection<String> list2 = new ArrayList<>();
Collection<Integer> list3 = new ArrayList<>();
list3.add(23);
list2.add("打击八楼他");
}
}
Collection集合常用API
这些方法即可以操作Set集合,也可以操作Queue(队列)和List集合,下面分别使用Collection集合接口的方法说明
方法名 说明
boolean add(E e) 向集合添加元素e,若指定集合元素改变了则返回true
boolean addAll(Collection<? extends E> c) 把集合C中的元素全部添加到集合中,若指定集合元素改变返回true
void clear() 清空所有集合元素
boolean contains(Object o) 判断指定集合是否包含对象o
boolean containsAll(Collection<?> c) 判断指定集合是否包含集合c的所有元素
boolean isEmpty() 判断指定集合的元素size是否为0
boolean remove(Object o) 删除集合中的元素对象o,若集合有多个o元素,则只会删除第一个元素
boolean removeAll(Collection<?> c) 删除指定集合包含集合c的元素
boolean retainAll(Collection<?> c) 从指定集合中保留包含集合c的元素,其他元素则删除
int size() 集合的元素个数
T[] toArray(T[] a) 将集合转换为T类型的数组
package com.itheima.d2_collectionApi;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
public class ApiDemo {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
//1.添加元素添加成功返回true
list.add("大家把");
list.add("大2把");
list.add("大3把");
System.out.println(list.add("大4把"));
System.out.println(list);
//2. 清空集合的元素
// list.clear();;
//3.判断集合是否为空,是空返回true 反之 false
list.isEmpty();
//4.获取集合大小
list.size();
//5.判断集合中是否包含某个元素
list.contains("Java");
//6.删除某个元素 如果有多个重复元素 默认删除前面的第一个!
list.remove("java");
System.out.println(list);
list.remove("Java");
System.out.println(list);
//7.把集合转换成数组 [java,..,...,..,]
Object[] arrs = list.toArray(); //为什么转换成Object类? 之后的反射技术会绕过编译阶段 直接无视你的泛型要求去添加数据
System.out.println("-------------拓展---------------");
Collection<String> c = new ArrayList<>();
c.add("123");
c.add("郑丹菲");
Collection<String> c1 = new ArrayList<>();
c1.add("铸币");
c1.add("电棍");
//addALL把c集合的元素全部倒入到c1中
c1.addAll(c);
System.out.println(c1);
}
}
Collection集合的遍历方式
1.迭代器
迭代器在java中的代表是Iterator,迭代器是集合的专用遍历方式。得到迭代器对象默认指向集合索引0
迭代器执行流程:
package com.tiheima.d3_Iterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
//迭代器在Java中的代表是Iterator 迭代器是集合的专用遍历方式
public static void main(String[] args) {
Collection<String> c= new ArrayList<>();
c.add("asd");
c.add("a12d");
c.add("a12dsd");
c.add("a12deee");
//1.得到当前集合的迭代器对象
//迭代器的默认位置在索引0 如果越界会提示异常
Iterator<String> it = c.iterator();
System.out.println(it.hasNext());//判断当前位置是否有元素
//String s = it.next(); //获取当前元素 并移动索引至下一个索引
// System.out.println(s);
//不要在一个循环中写两个 next() 否则会出现异常
while (it.hasNext()){
String rs = it.next();
System.out.println(rs);
}
}
}
2.forEach / 增强for循环
增强for循环,既可以遍历数组,也可以遍历集合。
实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口
forEach执行流程:
package com.tiheima.d3_Iterator;
import java.util.ArrayList;
import java.util.Collection;
public class IteratorDemo1 {
//增强for
public static void main(String[] args) {
Collection<String> c= new ArrayList<>();
c.add("asd");
c.add("a12d");
c.add("a12dsd");
c.add("a12deee");
//forEach 相当于迭代器的简化写法。 集合对象和数组对象都可以舒勇
for (String ele : c)
{
System.out.println(ele);
}
System.out.println("---------------");
int[] arr = {1,2,3,4};
for (int str : arr) {
System.out.println(str);
//修改无意义 不会影响数组元素值
// if (str == 3)
// {
// str = 5;
// }
}
}
}
forEach中修改变量:修改无意义,不会影响数组中的值。 因为他修改的只是方法的参数。
3.Lambda表达式
forEach方法的简化写法
package com.tiheima.d3_Iterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class IteratorDemo3 {
public static void main(String[] args) {
Collection<String> c= new ArrayList<>();
c.add("asd");
c.add("a12d");
c.add("a12dsd");
c.add("a12deee");
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//配合labmda表达式简化代码
c.forEach( s-> {
System.out.println(s);
});
}
}
forEach方法其实就是参数变量到堆内存中找到集合的地址再找到所需元素并且对其进行操作。
Collection集合存储自定义类型的对象
存储自定义类,将类名写入泛型中即可。
自定义类Movie 创建Movie类集合进行操作
Movie类JAVABEAN:
package com.itheima.d4_collection_object;
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;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return score
*/
public double getScore() {
return score;
}
/**
* 设置
* @param score
*/
public void setScore(double score) {
this.score = score;
}
/**
* 获取
* @return actor
*/
public String getActor() {
return actor;
}
/**
* 设置
* @param actor
*/
public void setActor(String actor) {
this.actor = actor;
}
public String toString() {
return "Movie{name = " + name + ", score = " + score + ", actor = " + actor + "}";
}
}
对其进行操作
package com.itheima.d4_collection_object;
import java.util.ArrayList;
import java.util.Collection;
public class Test {
public static void main(String[] args) {
Collection<Movie> movies = new ArrayList<>();
movies.add(new Movie("你好李焕英",9.5,"你,我,他"));
movies.add(new Movie("唐人街探案",8.5,"外日,我,你是?"));
movies.add(new Movie("刺杀小说家",8.6,"杨幂,雷佳音"));
//遍历集合容器中的每个对象
for (Movie m: movies ) {
System.out.println("片名" + m.getName());
System.out.println("评分" + m.getScore());
System.out.println("演员:" + m.getActor());
}
}
}
List系列集合
1.List集合特点 特有API
ArrayList LinkedList 有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引: 可以通过索引来操作元素(增删改查)
可重复: 集合中可以存储相同的元素
除了上面从Collection中继承的常用API,List实现类还有一些特有API,最常用的一些就是通过索引来进行操作的API
代码:
package com.itheima.d5_collection_list;
import java.util.ArrayList;
import java.util.List;
public class ListDemo {
//创建一个ArrayList集合对象
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Java");
list.add("Html");
list.add("Mysql");
list.add("Mysql");
//在某个索引插入元素
list.add(2,"黑马");
//根据索引删除元素,返回被删除元素
System.out.println(list.remove(1));
System.out.println(list);
// 根据索引获取元素: public E get(int index): 返回集合中指定位置的元素。
String rs = list.get(1);
System.out.println(rs);
//修改索引位置处的元素: public E set(int index, E element)
list.set(1,"嗯中之嗯");
System.out.println(list);
}
}
2.List集合遍历方式小结
一共有四种方式、
1.迭代器
2.增强for循环 / forEach
3.Lambda表达式
4.for循环遍历,因为List集合存在索引
package com.itheima.d5_collection_list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("丹菲");
list.add("丹菲猪");
list.add("丹菲猪猪");
/*
for循环
*/
System.out.println("-----------------------");
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
System.out.println(ele);
}
//迭代器
Iterator<String> i = list.iterator();
while (i.hasNext())
{
String s = i.next();
System.out.println(s);
}
//forEach
for (String ele: list) {
System.out.println(ele);
}
//Lambda表达式
list.forEach( e -> {
System.out.println(e);
});
}
}
3.ArrayList集合底层原理
ArrayList其实是基于数组来实现的,查询速度快,增删速度慢,
4.LinkedList集合底层原理
LinkedList是基于双链表实现的,对于首尾操作非常快,但是查询非常的慢。
LinkedList特有API
因此可以用这些API来实现队列和栈的操作:
package com.itheima.d5_collection_list;
import java.util.LinkedList;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args) {
// LinkedList 可以完成队列结构 和栈结构(双列表
// 做一个队列
LinkedList<String> list = new LinkedList<>();
//入队
list.addLast("一号");
list.addLast("二号");
list.addLast("三号");
System.out.println(list);
//出队
System.out.println(list.removeFirst());
System.out.println(list.removeFirst());
System.out.println(list.removeFirst());
//做一个栈
LinkedList<String> stack = new LinkedList<>();
//压栈
stack.push("第1颗"); //用栈写的话 push专业 其实push方法调用的还是addFirst
stack.push("第2颗");
stack.push("第3颗");
stack.push("第4颗");
System.out.println(stack);
//弹栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
}
}
集合的并发修改异常
如果想从集合中找到一些数据并进行删除 ,如何操作?是否存在问题呢?
当我们从集合中找到并删除元素的时候,可能出现一种并发修改异常。
代码:
package com.itheima.d6_collection_update_delete;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("黑马");
list.add("Java");
list.add("Java");
list.add("赵敏");
list.add("丹菲");
list.add("丹菲");
list.add("丹菲猪");
System.out.println(list);
//需求 删除全部的Java信息
Iterator<String> it = list.iterator();
while (it.hasNext())
{
String rs = it.next();
//删除Java 会出并发异常 之前学习Arraylist类删除数据的时候讲过这个 删除元素Arraylist会自动向前
if ("Java".equals(rs))
{
// list.remove(rs); 用集合删除会出毛病
it.remove(); // 删除迭代器所在位置的元素值 他在内部会自动控制。
}
}
System.out.println(list);
// forEach遍历删除(会出现问题, 这种无法解决,因为迭代器对象被藏起来了, 因此不能使用迭代器的remove)
// for (String rs : list) {
// list.remove(rs);
// }
}
}
那么这种情况是为什么发生的? 因为在上面讲过集合的特点,他在运行的时候是动态的,因此当你在删除一个元素之后,他的后一个元素会补上去,这样的话你此时的索引位置就会比即将访问的索引位置大1。
解决方法: 从后往前遍历,或者做完删除操作之后让索引位置-1
Set系列集合
set系列集合API基本上与Collection API相同 没有什么特有的方法
代码:
1.HashSet
HashSet 特点: 无序,无索引,不重复。 那么他是怎么实现无序无索引不重复的呢?这就要涉及到HashSet的底层原理了。
HashSet底层原理
代码:
package com.itheima.d1_Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class SetDemo1 {
public static void main(String[] args) {
//看看set集合的特点 : HashSet LinkedHashSet
// Set接口几乎没有特有的方法 用的都是Collection中的方法
//Set<String> sets = new LinkedHashSet<>(); 有序 不重复 五索引
Set<String> sets = new HashSet<>(); // 有序
sets.add("mySql");
sets.add("mySql");
sets.add("丹菲");
sets.add("丹菲猪");
System.out.println(sets);
}
}
HashSet特点的实现原理:
创建一个默认长度为16,默认加载因子为0.75的数组,数组名为table。默认加载因子的作用是什么,在源码中当数组存储长度达到16 * 0.75=12的时候,数组会自动扩容,每次扩容后的大小是原来的两倍。
无序:因为是根据元素的哈希值跟数组长度的取余计算出应该存储的位置,又因为每个元素的哈希值不同,因此所存的位置无序。
无索引:因为无序,因此就没有必要有索引了。
不重复:如果位置为null则将元素直接存储到哈希值相对应的存储位置,如果位置不为null,证明该位置上有元素,那么应调用equals方法进行比较(Object类默认equals方法是比较地址值),如果一样,则证明是相同元素,如果不一样,则存入数组(链表)。如果存的是自定义类,这个时候如果想比较是否是重复元素,那么就应该比较对象中的属性值,这个时候就要在类中复写其Equals和hashCode方法。那么为什么要复写hashCode方法呢?因为在复写后的HashCode源码中,如果参数中的属性值相同,则会返回相同的哈希值,又因为HashSet是根据当前元素的哈希值取余数组长度计算得到的位置,因此如果哈希值相同的话,就证明是相同的元素。
以上是JDK7及以前的HashSet原理,在JDK8版本更新后原理有了一些改变。
为什么在1.8版本之后组成原理多了一个红黑树呢? 因为在1.7版本的结构中,当一个位置上的元素下面挂的数据过多的时候(链表过长),查询性能就会降低,因此才在结构中引入红黑树。
package com.itheima.d1_Set;
import java.util.HashSet;
import java.util.Set;
/*
目标: 让Set集合把重复内容的对象去掉一个
*/
public class SetDemo2 {
public static void main(String[] args) {
//Set集合去重复原因, 先判断哈希值, 再用equals比较。
Set<Student> sets = new HashSet<>();
Student s1 = new Student("丹菲",20,'女');
Student s2 = new Student("丹菲猪",21,'母');
Student s3 = new Student("丹菲",20,'女');
sets.add(s1);
sets.add(s2);
sets.add(s3);
System.out.println(sets);
System.out.println(s1.hashCode());
System.out.println(s3.hashCode());
}
}
package com.itheima.d1_Set;
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;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return sex
*/
public char getSex() {
return sex;
}
/**
* 设置
* @param sex
*/
public void setSex(char sex) {
this.sex = sex;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", sex = " + sex + "}";
}
@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 && sex == student.sex && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
}
2.实现类 LinkedHashSet
LinkedListHashSet与HashSet的特点区别就是LinkedListHashSet集合中的元素是有序的。实现这一元素特点的底层原理是一位内,每个元素又额外的多了一个双链表的机制记录存储的顺序。为什么要用双链表呢? 因为双链表的存储机制就是存储p->next 和p->prior 也就是下一个元素的地址和上一个元素的地址,因此在从数组的第一个元素访问的时候,就可以通过双链表来找到第一个存储的元素。
代码:
package com.itheima.d1_Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class SetDemo3 {
public static void main(String[] args) {
Set<String> sets = new LinkedHashSet<>(); // 有序 不重复 无索引
//底层原理仍然是哈希表, 只是每个元素又额外的多了一个双链表的机制记录存储的顺序
sets.add("mySql");
sets.add("mySql");
sets.add("丹菲");
sets.add("丹菲猪");
System.out.println(sets);
}
}
3.实现类 TreeSet
TreeSet集合特点:不重复 无索引 可排序(独有特点)
对于自定义类,TreeSet是无法直接进行排序的,因此当你存储自定义类型的对象到TreeSet集合中,在你打印的时候程序会报错,那么如何解决这个问题呢?这个时候我们就要对自定义类制定排序规则,自定义规则的方式一共有两种,一种是在类中实现Comparable接口,在类中复写接口的比较方法,第二种是在创建对象的时候,往括号中传参。
第一种:在类中实现接口
注意: 接口为泛型接口 这个时候应该告诉接口的操作类。
第二种: 传参 使用匿名内部类的格式来复写方法。
注意点: 在返回值类型中用到了三目运算符的原因,因为当对比属性返回值为0的时候,TreeSet会误将其认为是同一种对象,这个时候只会保留一个元素。这个三目运算符的作用就是,当你返回值>=0 的时候,通通返回 1 否则就返回 -1,这样既符合了返回值的规则,也不会误删对象。
代码:
package com.itheima.d1_Set;
import java.util.Objects;
public class Apple implements Comparable<Apple> {
private String name;
private String color;
private double price;
private int weight;
public Apple() {
}
public Apple(String name, String color, double price, int weight) {
this.name = name;
this.color = color;
this.price = price;
this.weight = weight;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return color
*/
public String getColor() {
return color;
}
/**
* 设置
* @param color
*/
public void setColor(String color) {
this.color = color;
}
/**
* 获取
* @return price
*/
public double getPrice() {
return price;
}
/**
* 设置
* @param price
*/
public void setPrice(double price) {
this.price = price;
}
/**
* 获取
* @return weight
*/
public int getWeight() {
return weight;
}
/**
* 设置
* @param weight
*/
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Apple apple = (Apple) o;
return Double.compare(apple.price, price) == 0 && weight == apple.weight && Objects.equals(name, apple.name) && Objects.equals(color, apple.color);
}
@Override
public int hashCode() {
return Objects.hash(name, color, price, weight);
}
public String toString() {
return "Apple{name = " + name + ", color = " + color + ", price = " + price + ", weight = " + weight + "}";
}
@Override
public int compareTo(Apple o) {
// return o.getWeight() - this.getWeight() ; // 降序排序
return this.getWeight() - o.getWeight() >= 0 ? 1 : -1; //升序排序
// 因为如果返回零的话 TreeSet会自动认为 两个对象为同一对象 这样他会删除其中一个, 这个时候就可以用三目运算符来对其进行一个判断
//如果大于等于0 一律返回 1 这样weight值相等的不同对象就排在前面
}
}
package com.itheima.d1_Set;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class SetDemo4 {
public static void main(String[] args) {
Set<Integer> sets = new TreeSet<>(); // 不重复 无索引 可排序
//可排序功能默认对 Integer 和 Double 类型有效
sets.add(25);
sets.add(38);
sets.add(46);
sets.add(17);
System.out.println(sets);
//对自定义类操作
System.out.println("-------------------------");
//方式二: 集合自带比较器对象
// Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {
// @Override
// public int compare(Apple o1, Apple o2) {
// //降序排序 如果类中复写比较方法
// // 对象中参数也复写方法 会优先调用参数构造器中复写的规则
// return o2.getWeight() - o1.getWeight();
//注意 浮点型数据建议直接使用Double.compare比较
// return Double.Compare(o1.getPrice() , o2.getPrice() ) // 升序
// }
// });
//简写格式
Set<Apple> apples = new TreeSet<>((o1, o2) -> {
return o2.getWeight() - o1.getWeight();
}
//降序排序 如果类中复写比较方法
// 对象中参数也复写方法 会优先调用参数构造器中复写的规则
);
apples.add(new Apple("红富士","红",9.9,500));
apples.add(new Apple("绿富士","绿",1.9,400));
apples.add(new Apple("青富士","青",2.9,300));
apples.add(new Apple("黄富士","黄",3.9,500));
//直接打印集合会报错 因为是自定义类 TreeSet不知道你要用什么规则来进行排序
System.out.println(apples);
//自定义规则的两种方法, 第一 在类中实现Comparable接口 在类中复写方法
}
}
泛型的深入
泛型的作用:可以在编译阶段约束操作的数据类型,并进行检查。
泛型只支持引用数据类型。集合体系的全部接口和实现类都是支持泛型的使用的
泛型的好处:统一数据类型
自定义泛型类
泛型可以在很多地方进行定义
泛型类: 定义了类的同时定义了泛型的类就是泛型类
格式: public class A <T>{ }; 此处泛型中的变量T可以随便写为任意标识:如 E T K V等
作用:编译阶段可以指定任意数据类型,类似于集合的作用。
原理:把出现泛型变量的地方全部替换成传输的真实数据类型
这种方法也叫做包装类思想,以后的代码风格会用到。
package com.itheima.d7_genericity_class;
import java.util.ArrayList;
public class MeArrayList <T>{
private ArrayList lists = new ArrayList<>();
public void add(T t)
{
lists.add(t);
}
public void remove(T t){
lists.remove(t);
}
}
package com.itheima.d7_genericity_class;
public class Test {
//模拟ArrayList定义一个MyArrayList 关注泛型设计
public static void main(String[] args) {
MeArrayList<String> list = new MeArrayList<>();
list.add("Java");
list.add("JB");
list.add("DJB");
list.add("ASD");
list.remove("DJB");
MeArrayList<Integer> list1 = new MeArrayList<>();
list1.add(12);
list1.add(23);
list1.add(42);
// list1.add("asd"); 报错
}
}
自定义泛型方法
package com.itheima.d7_genericity_class;
public class MyArrayList {
public static void main(String[] args) {
String[] name = {"璐璐","风女","男枪"};
printArray(name);
}
public static <T> T[] getArr(T[] arr)
{
return arr;
}
private static <T> void printArray(T[] name) {
if (name!=null)
{
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < name.length; i++) {
sb.append(name[i]).append(i == name.length - 1 ? "" : ",");
}
sb.append("]");
System.out.println(sb);
}
}
}
自定义泛型接口
泛型通配符 上下限
可变参数
这里为什么可变参数必须放在形参列表的后面:因为如果可变参数在前面的话,java会将你后面的形参也当成他的可变参数,这样就分不清楚了。
package d2_params;
import java.util.Arrays;
public class MethodDemo {
/*
可变参数 可变参数用在形参中可以接收多个数据
可变参数的格式: 数据类型....参数名称
*/
public static void main(String[] args) {
sum();//不传
sum(10);//传一个
sum(10,20,30);//传多个
sum(new int[]{10});//传数组
}
//可变参数格式,可变参数必须放在形参后面 不然会把偶从
public static void sum(int...nums){
System.out.println(nums.length);
System.out.println(Arrays.toString(nums));
}
}
Collections工具类
Collections工具类 是帮助集合进行操作的工具类 他并不是集合
addALL 给集合对象批量添加元素
中的第一个参数泛型里面是通配符,意思是你只能存放T类或者T的父类,后面是T类或T的父类的元素。
shuffle方法:按照默认规则给List集合中的元素顺序打乱 底层源码:随机索引,交换数组位置
参数为List接口实现类,那么为什么不能是set呢? 因为set接口和其实现类的特点就是无序的,所以根本不需要去打乱 。
package d3_collections;
import com.itheima.d1_Set.Apple;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo1 {
public static void main(String[] args) {
/*
Collections API addALL(给集合对象批量添加) sort
public static<T> boolean addALL (Collection<? super T>c, T... elements) 添加所有
public static void shuffle(List<?> list) 打乱集合顺序
public static<T> void sort(List<?> list) 将集合中元素按照默认规则排序
public static<T> void sort(List<?> list, Comparator<? super T>c) 将集合中元素按照指定规则排序
作用: Collections不属于集合 是用来操作集合的工具类 里面方法都是静态方法 因此可以直接调用 无需创建对象
*/
List<Apple> apples = new ArrayList<>();
Apple p1 = new Apple("红富士","红",9.9,500);
Apple p2 = new Apple("绿富士","绿",1.9,400);
Apple p3 = new Apple("青富士","青",2.9,300);
Apple p4 = new Apple("黄富士","黄",3.9,500);
Collections.addAll(apples, p1,p2,p3,p4);
System.out.println(apples);
List<Integer> it = new ArrayList<>();
//批量添加
Collections.addAll(it,56,4564,25,456,234,15,3);
System.out.println(it);
//打乱顺序 源码: 随机索引交换
Collections.shuffle(it);
System.out.println(it);
//按默认规则排序 默认升序
Collections.sort(it);
System.out.println(it);
}
}
Collections工具类对List集合的两种排序方式 为什么不对SET操作呢? 因为Set实现类是无序的,而且在Set的实现类中 有TreeSet这个实现类 他可以自动实现排序功能 因此就不需要再用工具类实现了。
方式一 在类中实现比较器接口,并在类中重写比较规则 与TreeSet 区别:List集合中的元素可以重复,因此就不需要再用到再TreeSet中使用的三目运算符进行判断了。
代码:
方式二:调用方法并传参 后面为比较器接口。
代码:
package d3_collections;
import com.itheima.d1_Set.Apple;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsDemo2 {
public static void main(String[] args) {
List<Apple> apples = new ArrayList<>();
Apple p1 = new Apple("红富士","红",9.9,500);
Apple p2 = new Apple("绿富士","绿",1.9,400);
Apple p3 = new Apple("青富士","青",2.9,300);
Apple p4 = new Apple("黄富士","黄",3.9,500);
Collections.addAll(apples, p1,p2,p3,p4);
System.out.println(apples);
//方式一 在类中重写排序规则 再用集合工具类调用
//方式二
Collections.sort(apples, new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return Double.compare(o1.getPrice(),o2.getPrice());
}
});
System.out.println(apples);
}
}
MAP集合
map集合是完全区别与Collection体系的另一钟集合
Map集合概述
Map集合特点
代码:
package d5_map;
import java.util.HashMap;
import java.util.Map;
public class mapDemo {
public static void main(String[] args) {
//无序 五索引 不重复
Map<String, Integer> maps = new HashMap<>();
maps.put("鸿星尔克",100);
maps.put("HuaWei",200);
maps.put("怪零",300);
maps.put("吴京",400);
System.out.println(maps);
}
}
Map常用API如下
代码:
package d5_map;
import java.util.*;
public class MapDemo01 {
public static void main(String[] args) {
//无序 五索引 不重复
Map<String, Integer> maps = new HashMap<>();
maps.put("鸿星尔克", 100);
maps.put("HuaWei", 200);
maps.put("怪零", 300);
maps.put("吴京", 400);
System.out.println(maps);
//
// //清空集合
// maps.clear();
// System.out.println(maps);
//判断集合是否为空 为空返回true 反之false
System.out.println(maps.isEmpty());
//根据键获取对应值 public V get(Object key)
Integer key = maps.get("Huawei");
System.out.println(key);
System.out.println(maps.get("吴京"));
System.out.println(maps.get("怪零"));
System.out.println("-----------------------------");
//根据键删除元素(删除键 会返回键的值)
System.out.println(maps.remove("吴京"));
System.out.println(maps);
//判断是否包含某个键
System.out.println(maps.containsKey("吴京"));
//判断是福哦包含某个值
System.out.println(maps.containsValue(100));
// 获取全部键 的集合, public Set<K> keySet()
Set<String> keys = maps.keySet();
System.out.println(keys);
//获取全部值的集合 Collections 《V》 Values 这里面用Collections存储是因为怕又重复的给删掉
Collection<Integer> value = maps.values();
//集合大小
System.out.println(maps.size());
// 合并其他Map集合 (拓展)
System.out.println("--------------------");
System.out.println("maps:" + maps);
Map<String,Integer> maps1 = new HashMap<>();
maps1.put("丹菲",5);
maps1.put("昊京",25);
maps1.put("Trump",20);
maps1.put("Simple",10);
System.out.println("maps1: " + maps1);
maps.putAll(maps1);
System.out.println(maps);
}
}
Map集合的遍历方式(三种)
1.通过键找值 2.键值对遍历 3.Lambda表达式
代码:
package d5_map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
public class MapDemo2 {
public static void main(String[] args) {
// maps遍历流程
// 方式一 : 键找值
Map<String, Integer> maps = new HashMap<>();
maps.put("鸿星尔克",100);
maps.put("HuaWei",200);
maps.put("怪零",300);
maps.put("吴京",400);
System.out.println(maps);
Set<String> sets = maps.keySet();
System.out.println(sets);
for (String key : sets) {
int value = maps.get(key);
System.out.println(key +"=====>"+value);
}
System.out.println("----------------");
//将键值对分装成 Entry实现类对象 然后再放到Set中
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
for (Map.Entry<String, Integer> entry: entries) {
String key = entry.getKey();
int value = entry.getValue();
System.out.println(key +"======>"+value);
}
System.out.println("-----------------");
// maps.forEach(new BiConsumer<String, Integer>() {
// @Override
// public void accept(String s, Integer integer) {
// System.out.println(s +"---->"+ integer );
// }
// });
maps.forEach((k,v) -> {System.out.println(k +"-->"+v);});
}
}
Map的实现类
1.HashMap
值得注意的是 HashMap底层判断是否为同一元素的方式也是依赖重写hashCode和Equals方法,但是是保证键的唯一。
代码:
package d7_map_impl;
import com.itheima.d1_Set.Student;
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
/*
HashMap的底层原理是 保证键的唯一性。 如果键要存储的是自定义对象,需要重写hashCode和Equals方法
*/
public static void main(String[] args) {
Map<Student,String> stus = new HashMap<>();
Student s1 = new Student("丹菲",20,'女');
Student s2 = new Student("丹菲猪",21,'母');
Student s3 = new Student("丹菲",20,'女');
stus.put(s1,"上海");
stus.put(s2,"广州");
stus.put(s3,"百京");
//按属性值判断同一对象 重复的话 后面的覆盖前面的
System.out.println(stus);
}
}
2.LinkedList
代码:
package d7_map_impl;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapDemo {
public static void main(String[] args) {
//有序 不重复 无索引, 键相同后面的会覆盖前面的
Map<String, Integer> maps = new LinkedHashMap<>();
maps.put("鸿星尔克", 100);
maps.put("HuaWei", 200);
maps.put("HuaWei",500);
maps.put("怪零", 300);
maps.put("吴京", 400);
System.out.println(maps);
}
}
、
3.TreeMap
package d7_map_impl;
import com.itheima.d1_Set.Apple;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
Map<Apple,String> apples = new TreeMap<>(new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return Double.compare(o2.getPrice() , o1.getPrice());
}
});
//两种排序方式,第一种类中排序 第二种创建对象 参数构造器
apples.put(new Apple("红富士","红",9.9,500),"日本");
apples.put(new Apple("绿富士","绿",1.9,400),"阜阳");
apples.put(new Apple("青富士","青",2.9,300),"美国");
apples.put(new Apple("黄富士","黄",3.9,500),"洛阳");
System.out.println(apples);
}
}
小知识点:集合的嵌套
案例:统计同学们想去的地方
package d7_map_impl;
import java.util.*;
public class MapTest {
/*
投票 集合嵌套
*/
public static void main(String[] args) {
//存储一个Map集合存储学生选项
Map<String, List<String>> data = new HashMap<>();
List<String> list = new ArrayList<>();
Collections.addAll(list,"A","B","C","D");
List<String> list1 = new ArrayList<>();
Collections.addAll(list1,"A","B","C");
List<String> list2 = new ArrayList<>();
Collections.addAll(list2,"C", "D");
data.put("大壮",list);
data.put("丹菲",list1);
data.put("蛋蛋",list2);
//统计人数
Map<String,Integer> maps = new HashMap<>();
//获取每人的投票信息
Collection<List<String>> values = data.values();
System.out.println(values);
for (List<String> value :values) {
for (String s : value)
{
if (maps.containsKey(s))
{
maps.put(s,maps.get(s) +1 ) ;
}else {
maps.put(s,1);
}
}
}
System.out.println(maps);
}
}