数组和集合的区别
- 相同点
都是容器,可以存储多个数据 - 不同点
数组长度是不可变的,集合长度是可变的
数组可以存储基本数据类型和引用数据类型
集合只能存储引用数据类型,如果要存储基本数据类型,需要 对应的包装类。
集合类体系结构
Collection集合概述和使用
- Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set()和List)实现。
- 创建Collection集合对象
- 多态的方式
- 具体的实现类ArrayList
- Collection集合常用方法
Collection集合的遍历
- 迭代器介绍
- 迭代器,集合专用遍历方式
- Iterator iterator();返回集合中元素的迭代器,通过集合对象的iterator()方法得到
- Iterator中常用方法
boolean hasNext();判断当前位置是否有元素可以被取出。
E.next(); 获取当前位置的元素,将迭代器对象移向下一个索引 - Collection集合的遍历
removeIf方法
根据条件进行删除
将条件以Lambda表达式方式传入到方法中
满足就删除,不满足就不删除
collection.removeIf(
(String s)-> return s.length()==3);
满足条件s.length()==3则会删除元素。
public class IteratorDemo1 {
public static void main(String[] args) {
//创建集合对象
Collection<String> c = new ArrayList<>();
//添加元素
c.add("hello");
c.add("world");
c.add("java");
c.add("javaee");
//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
Iterator<String> it = c.iterator();
//用while循环改进元素的判断和获取
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
在迭代的过程中不能对集合长度进行就该,否则会抛出异常
例子
public static void main(String[] args) {
Collection<String> arr=new ArrayList<String>();
arr.add("张无忌");
arr.add("张翠山");
arr.add("赵敏");
arr.add("杨不悔");
Iterator<String> iterator = arr.iterator();
while(iterator.hasNext()){
String str = iterator.next();
if(str.equals("张翠山")){
arr.remove(str); //这里就是对集合长度进行修改了
}
System.out.println(str);
}
}
///修改后代码/
public static void main(String[] args) {
Collection<String> arr = new ArrayList<String>();
arr.add("张无忌");
arr.add("张翠山");
arr.add("赵敏");
arr.add("杨不悔");
Iterator<String> iterator = arr.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if (str.equals("张翠山")) {
iterator.remove();
}
}
System.out.println(arr);
}
增强for循环
介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组Collection集合的遍历
格式
- for(集合/数组中元素的数据类型 变量名:集合/数组名){
- //已经将当前遍历到的元素封装到变量中了,直接使用变量即可}
代码
public class MyCollectonDemo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
//1,数据类型一定是集合或者数组中元素的类型
//2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素
//3,list就是要遍历的集合或者数组
for(String str : list){
System.out.println(str);
}
}
}
单列可以使用增强for和迭代器,双列的不可以。
增强for技巧
list.for IDEA中直接生成
三种循环使用场景
- 如果需要操作索引,使用普通for循环
- 如果在遍历的过程中需要删除元素,请使用迭代器
- 如果仅仅想遍历,那么使用增强for
List集合
List集合的概述和特点
- List集合的概述
- 有序集合,这里的有序是指存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复元素
- List集合的特点
- 存取有序
- 可以重复
- 有索引
List集合特有方法
数据结构
- 栈结构
先进后出 - 队列结构
先进先出 - 数组结构
查询快、删除慢 - 队列结构
查询慢、增删快
List集合的实现类
特点
- ArrayList集合
底层是数组结构实现,查询快,增删慢 - LinkedList集合
底层是链表结构实现,查询慢,增删快
List集合概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
List集合特点
- 有序:存储和取出的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
LinkedList集合的特有功能
泛型
- 提供了编译时类型安全检测机制
- 范型的好处
- 把运行时的问题提前到编译时期
- 避免了强制转换类型
- 范型的定义格式
<类型>:指定一个类型的格式,尖括号里任意书写,一般只写一个字母
<类型1,类型2.,>:指定多种类型的格式,多种类型之间用逗号隔开
泛型类
- 定义格式
修饰符 class 类名<类型>{}
public class Generic<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
泛型方法
- 定义格式
修饰符 <类型> 返回值类型 方法名(类型 变量名){}
public class Generic {
public <T> void show(T t) {
System.out.println(t);
}
}
public class GenericDemo2 {
public static void main(String[] args) {
Generic g = new Generic();
g.show("柳岩");
g.show(30);
g.show(true);
g.show(12.34);
}
}
泛型接口
定义格式
修饰符 interface 接口名<类型>{ }
public interface Generic<T> {
void show(T t);
}
- 泛型接口实现类
定义实现类时,定义和接口相同泛型,创建实现类对象时明确泛型的具体类型
public class GenericImpl1<T> implements Generic<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
- 泛型接口实现类2
- 定义实现类时,明确泛型的具体类型
public class GenericImpl2 implements Generic<Integer>{
@Override
public void show(Integer t) {
System.out.println(t);
}
}
- 测试类
public class GenericDemo3 {
public static void main(String[] args) {
GenericImpl1<String> g1 = new GenericImpl<String>();
g1.show("林青霞");
GenericImpl1<Integer> g2 = new GenericImpl<Integer>();
g2.show(30);
GenericImpl2 g3 = new GenericImpl2();
g3.show(10);
}
}
类型通配符
类型通配符: <?>
ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
类型通配符上限: <? extends 类型>
ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型
类型通配符下限: <? super 类型>
ArrayListList <? super Number>: 它表示的类型是Number或者其父类型
泛型通配符的使用
public class GenericDemo4 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Number> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
method(list1);
method(list2);
method(list3);
method(list4);
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型通配符: 此时的泛型?,可以是任意类型
public static void method(ArrayList<?> list){}
// 泛型的上限: 此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(ArrayList<? extends Number> list){}
// 泛型的下限: 此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(ArrayList<? super Number> list){}
}
定义一个集合,并把集合(集合里面的元素是Integer)转成存有相同元素的数组,并将结果输出在控制台。(可以使用Object[]数组类型接收转换的数组)
public class CollectionTest03 {
public static void main(String[] args) {
//定义集合,添加数据
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(100);
list.add(200);
list.add(300);
//Object[] toArray()转换成一个Object数组
Object[] obj = list.toArray();
// 遍历数组
for (int i = 0; i < obj.length; i++) {
System.out.println(obj[i]);
}
}
}
Set
set集合概述和特点
- 不可以存储重复元素
- **没有索引,不能使用普通for循环**
集合的使用
public class MySet1 {
public static void main(String[] args) {
//创建集合对象
Set<String> set = new TreeSet<>();
//添加元素
set.add("ccc");
set.add("aaa");
set.add("aaa");
set.add("bbb");
// for (int i = 0; i < set.size(); i++) {
// //Set集合是没有索引的,所以不能使用通过索引获取元素的方法
// }
//遍历集合
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐");
for (String s : set) {
System.out.println(s);
}
}
}
TreeSet
集合概述和特点
-不可以存储重复元素
-没有索引
-可以将元素按照规则进行排序
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator) :根据指定的比较器进行排序
Treeset集合基本使用
public class TreeSetDemo01 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Integer> ts = new TreeSet<Integer>();
//添加元素
ts.add(10);
ts.add(40);
ts.add(30);
ts.add(50);
ts.add(20);
ts.add(30);
//遍历集合
for(Integer i : ts) {
System.out.println(i);
}
}
}
自然排序Commparable的使用
案例需求
存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
实现步骤
- 使用空参构造创建TreeSet集合
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的 - 自定义的Student类实现Comparable接口
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法 - 重写接口中的compareTo方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Student implements Comparable<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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照对象的年龄进行排序
//主要判断条件: 按照年龄从小到大排序
int result = this.age ‐ o.age;
//次要判断条件: 年龄相同时,按照姓名的字母顺序排序
result = result == 0 ? this.name.compareTo(o.getName()) : result;
return result;
}
}
//测试类
public class MyTreeSet2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> ts = new TreeSet<>();
//创建学生对象
Student s1 = new Student("zhangsan",28);
Student s2 = new Student("lisi",27);
Student s3 = new Student("wangwu",29);
Student s4 = new Student("zhaoliu",28);
Student s5 = new Student("qianqi",30);
//把学生添加到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
//遍历集合
for (Student student : ts) {
System.out.println(student);
}
}
}
自然排序的Comparable实现逻辑
@Override
public int compareTo(Student o) {
//按照对象的年龄进行排序
//主要判断条件: 按照年龄从小到大排序
int result = this.age ‐ o.age; //这一条this表示当前存入的对象,o表示已经存入的对象.
//次要判断条件: 年龄相同时,按照姓名的字母顺序排序
result = result == 0 ? this.name.compareTo(o.getName()) : result;
return result;
}
}
自然排序简单原理图
- 如果返回值为负,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边.
比较器排序Comparator的使用
存储老师对象并遍历,创建TreeSet集合使用带参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
实现步骤
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class MyTreeSet4 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
//o1表示现在要存入的那个元素
//o2表示已经存入到集合中的元素
//主要条件
int result = o1.getAge() ‐ o2.getAge();
//次要条件
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
//创建老师对象
Teacher t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
//把老师添加到集合
ts.add(t1);
ts.add(t2);
ts.add(t3);
ts.add(t4);
//遍历集合
for (Teacher teacher : ts) {
System.out.println(teacher);
}
}
}
两种方法总结
两种比较方式小结
自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行
排序
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class MyTreeSet4 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
//o1表示现在要存入的那个元素
//o2表示已经存入到集合中的元素
//主要条件
int result = o1.getAge() ‐ o2.getAge();
//次要条件
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
//创建老师对象
Teacher t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
//把老师添加到集合
ts.add(t1);
ts.add(t2);
ts.add(t3);
ts.add(t4);
//遍历集合
for (Teacher teacher : ts) {
System.out.println(teacher);
}
}
}
在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
两种方式中关于返回值的规则
如果返回值为负数,表示当前存入的元素是较小值,存左边
如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
如果返回值为正数,表示当前存入的元素是较大值,存右边
compareTo比较字符串
将从小到大变为从大到小,只需要将this和o对调就可
int result = o.age ‐ this.age;
HashSet
HashSet集合概述和特点
- 底层数据结构是哈希表
- 存取无序
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
HashSet集合的基本应用
存储字符串并遍历
public class HashSetDemo {
public static void main(String[] args) {
//创建集合对象
HashSet<String> set = new HashSet<String>();
//添加元素
set.add("hello");
set.add("world");
set.add("java");
//不包含重复元素的集合
set.add("world");
//遍历
for(String s : set) {
System.out.println(s);
}
}
}
哈希值
- 哈希值简介
- 根据对象的地址或者字符串或者数字算出来的int类型的数值
- 如何获取哈希值
- Object类中的public int hashCode();返回对象的哈希码值
- 哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 在默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法,可以实现不同对象的哈希值相同,如果不同的对象属性值是一样的,那么计算出来的哈希值也是一样的.
哈希表结构
数组+链表
- JDK1.8以后
- 节点个数少于等于8个
数组+链表 - 节点个数多余8个
数组+红黑树
- 节点个数少于等于8个
Map集合
Map集合概述和特点
- Map集合概述
interface Map<K,V> K:键的类型;V:值的类型 - Map集合的特点
- 双列集合,一个键对应一个值
- 键不可以重复,值可以重复
- 无序的
- Map集合的基本使用
public class MapDemon01 {
public static void main(String[] args){
Map<String,String> map = new HashMap<String,String>();
//V put(K key, V value) 将指定的值与该映射中的指定键相关联
map.put("itheima001","林青霞");
map.put("itheima002","张曼玉");
map.put("itheima003","王祖贤");
map.put("itheima003","柳岩");
//输出集合对象
System.out.println(map);
}}
Map集合的基本功能
- 方法介绍
Map集合的获取功能
- 方法介绍
public class MapDemo03{
public static void main(String[] args){
//创建集合对象
Map<String,String> map = new HashMap<String,String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//get(Object key);根据键值取值
System.out.println(map.get("张无忌"));
// System.out.println(map.get("张三丰"));
//Set<K> keySet():获取所有键的集合
Set<String> keySet = map.keySet();
for(String key:keySet){
System.out.println(key);}}
//Collection<V> values();获取所有值的集合
Collection<String> values = map.values();
for(String value : values){
System.out.println(value);}
}
public class MapDemo02 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
Map集合的遍历方式
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取到每一个键。用增强for实现
- 根据键去找值。用get(Object key)方法实现
public class MapDemo01 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键的集合。用keySet()方法实现
<String> keySet = map.keySet();
//遍历键的集合,获取到每一个键。用增强for实现
for (String key : keySet) {
//根据键去找值。用get(Object key)方法实现
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
Map集合的遍历(方式2)
- 获取所有键值对对象的集合
- Set> entrySet():获取所有键值对对象的集合
- 遍历键值对对象的集合,得到每一个键值对对象
- 用增强for实现,得到每一个Map.Entry
根据键值对对象获取键和值
用getKey()得到键
用getValue()得到值
代码实现
class MapDemo02 {
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
}
HashMap集合
概述和特点
- HashMap底层是哈希表结构
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键要存储的是自定义对象,需要重写hashCode和equals方法
HashMap和HashSet是一样的存储方式,但是HashMap只会比较键的值,当不为nuii是添加,当计算的键值(比如Student对象)相同时,会比较键的属性值,如name,age.如果这也一样,就会替换到原先的value,如果key相同但是属性值不相同,就会将value挂在后边。这里比较的是Entry对象,键值对。但是计算hash值时只计算键的hash值,和值无关。
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;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
测试
public class HashMapDemo {
public static void main(String[] args) {
//创建HashMap集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();
//创建学生对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
Student s4 = new Student("王祖贤", 33);
//把学生添加到集合
hm.put(s1, "西安");
hm.put(s2, "武汉");
hm.put(s3, "郑州");
hm.put(s4, "北京");
//遍历集合
Set<Student> keySet = hm.keySet();
for (Student key : keySet) {
String value = hm.get(key);
System.out.println(key.getName() + "," + key.getAge() + "," + value);
}
}
}
forEach方法遍历Map
hm.forEach((Student key,String value)->{
System.out.println(key+value);});
TreeMap
特点和概述
- TreeMap底层是红黑树结构
- 依赖自然排序或者比较器排序,对键进行排序
- 如果键存储的是自定义对象,需要实现Comoarable接口或者创建TreeMap对象时给出比较器规则
TreeMap集合应用案例一
- 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
- 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
public class Student implements Comparable<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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照年龄进行排序
int result = o.getAge() ‐ this.getAge();
//次要条件,按照姓名排序。
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}
}
测试类
class Test1 {
public static void main(String[] args) {
// 创建TreeMap集合对象
TreeMap<Student,String> tm = new TreeMap<>();
// 创建学生对象
Student s1 = new Student("xiaohei",23);
Student s2 = new Student("dapang",22);
Student s3 = new Student("xiaomei",22);
// 将学生对象添加到TreeMap集合中
tm.put(s1,"江苏");
tm.put(s2,"北京");
tm.put(s3,"天津");
// 遍历TreeMap集合,打印每个学生的信息
tm.forEach(
(Student key, String value)‐>{
System.out.println(key + "‐‐‐" + value);
}
);
}
}
比较器模式不需要实现接口,直接使用匿名内部类的方式实现。
可变参数
可变参数:就是形参的个数是可以变化的
- 格式:修饰符 返回值类型 方法名(数据类型… 变量名) { }
- 方法的参数类型已经确定,个数不确定,我们可以使用可变参数
可变参数注意事项
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
//存在,表示当前字符已经出现过了
//先获取这个字符已经出现的次数
Integer count = tm.get(c);
//自增,表示这个字符又出现了依次
count++;
//将自增后的结果再次添加到集合中。
tm.put(c,count);
}
}
// a(5)b(4)c(3)d(2)e(1)
//System.out.println(tm);
tm.forEach(
(Character key,Integer value)‐>{
System.out.print(key + "(" + value + ")");
}
);
}
}
修饰符 返回值类型 方法名(数据类型… 变量名) { }
public class ArgsDemo01 {
public static void main(String[] args) {
System.out.println(sum(10, 20));
System.out.println(sum(10, 20, 30));
System.out.println(sum(10, 20, 30, 40));
System.out.println(sum(10,20,30,40,50));
System.out.println(sum(10,20,30,40,50,60));
System.out.println(sum(10,20,30,40,50,60,70));
System.out.println(sum(10,20,30,40,50,60,70,80,90,100));
}
// public static int sum(int b,int... a) {
// return 0;
// }
public static int sum(int... a) {
int sum = 0;
for(int i : a) {
sum += i;
}
return sum;
}
}
创建不可变集合
方法介绍
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合
- 这个集合不能添加,不能删除,不能修改
- 但是可以结合集合的带参构造,实现集合的批量添加 - 在Map接口中,还有一个ofEntries方法可以提高代码的阅读性
- 首先会把键值对封装成一个Entry对象,再把这个Entry对象添加到集合当中
// }
public static int sum(int... a) {
int sum = 0;
for(int i : a) {
sum += i;
}
return sum;
}
}
public class MyVariableParameter4 {
public static void main(String[] args) {
// static <E> List<E> of(E…elements) 创建一个具有指定元素的List集合对象
//static <E> Set<E> of(E…elements) 创建一个具有指定元素的Set集合对象
//static <K , V> Map<K,V> of(E…elements) 创建一个具有指定元素的Map集合对象
//method1();
//method2();
//method3();
//method4();
}
private static void method4() {
Map<String, String> map = Map.ofEntries(
Map.entry("zhangsan", "江苏"),
Map.entry("lisi", "北京"));
System.out.println(map);
}
private static void method3() {
Map<String, String> map = Map.of("zhangsan", "江苏", "lisi", "北京", "wangwu", "天
津");
System.out.println(map);
}
private static void method2() {
//传递的参数当中,不能存在重复的元素。
Set<String> set = Set.of("a", "b", "c", "d","a");
System.out.println(set);
}
private static void method1() {
List<String> list = List.of("a", "b", "c", "d");
System.out.println(list);
//list.add("Q");
//list.remove("a");
//list.set(0,"A");
//System.out.println(list);
// ArrayList<String> list2 = new ArrayList<>();
// list2.add("aaa");
// list2.add("aaa");
// list2.add("aaa");
// list2.add("aaa");
//集合的批量添加。
//首先是通过调用List.of方法来创建一个不可变的集合,of方法的形参就是一个可变参数。
//再创建一个ArrayList集合,并把这个不可变的集合中所有的数据,都添加到ArrayList中。
ArrayList<String> list3 = new ArrayList<>(List.of("a", "b", "c", "d"));
System.out.println(list3);
}
}
Map遍历的原理
Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在Map中是一一对应关系,这一对对象又称做Map 中的一个Entry(项)。Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。
Stream流
Stream流的好处
- 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤、打印
- Stream流把真正的函数式编程风格引入Java中
- 代码简洁
Stream流的三类方法
- 获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
- 中间操作
- 流水线上的操作
- 一次操作完之后,还可以继续进行其他操作
- 终结操作
- 一个Stream流只能有一个终结方法
- 是流水线上的最后一个操作
- 生成Stream流的方式
- Collection体系集合
使用默认方法stream()生成流, default Stream stream()
- Map体系集合
把Map转成Set集合,间接的生成流
- 数组
通过Arrays中的静态方法stream生成流
- 同种数据类型的多个数据
通过Stream接口的静态方法of(T... values)生成流
代码示例
public class StreamDemo {
public static void main(String[] args) {
//Collection体系的集合可以使用默认方法stream()生成流
List<String> list = new ArrayList<String>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<String>();
Stream<String> setStream = set.stream();
//Map体系的集合间接的生成流
Map<String,Integer> map = new HashMap<String, Integer>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组可以通过Arrays中的静态方法stream生成流
String[] strArray = {"hello","world","java"};
Stream<String> strArrayStream = Arrays.stream(strArray);
//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流
Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
Stream<Integer> intStream = Stream.of(10, 20, 30);
}
}
Stream流中间操作方法
filter代码示例
public class StreamDemo01 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:把list集合中以张开头的元素在控制台输出
list.stream().filter(s ‐> s.startsWith("张")).forEach(System.out::println);
System.out.println("‐‐‐‐‐‐‐‐");
//需求2:把list集合中长度为3的元素在控制台输出
list.stream().filter(s ‐> s.length() == 3).forEach(System.out::println);
System.out.println("‐‐‐‐‐‐‐‐");
//需求3:把list集合中以张开头的,长度为3的元素在控制台输出
list.stream().filter(s ‐> s.startsWith("张")).filter(s ‐> s.length() ==
3).forEach(System.out::println);
}
}
//filter中接口Predicate接口只有一个抽象方法test
list.stream().filter(new Predicate<String>() {
public boolean test(String s){
boolean result = s.startsWith("张");
return result;
}})
//所以我们可以用Lambda表达式来简化方法
//s表示流中的每一个数据,后边的表示条件
list.stream.filter(s->s.startwith("z")).forEach(s->System.out.println(s));
limit&skip代码演示
public class StreamDemo02 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:取前3个数据在控制台输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("‐‐‐‐‐‐‐‐");
//需求2:跳过3个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
System.out.println("‐‐‐‐‐‐‐‐");
//需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
list.stream().skip(2).limit(2).forEach(System.out::println);
}
}
concat&distinct代码演示
public class StreamDemo03 {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:取前4个数据组成一个流
Stream<String> s1 = list.stream().limit(4);
//需求2:跳过2个数据组成一个流
Stream<String> s2 = list.stream().skip(2);
//需求3:合并需求1和需求2得到的流,并把结果在控制台输出
// Stream.concat(s1,s2).forEach(System.out::println);
//需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
Stream.concat(s1,s2).distinct().forEach(System.out::println);
}
}
Stream流终结操作方法
终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
void forEach(Consumer action):对此流的每个元素执行操作
Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
//forEach方法的底层会循环获取到流中的每一个数据
//并循环调用accept方法,并把每一个数据传递给accept方法
//s就依次表示了流中的每一个数据
//所以我们要在accept方法中,写上处理逻辑就可
//Lambda表达式简化格式
//因为Consumer接口中,只有一个accept方法
long count = list.stream().filter(s ‐> s.startsWith("张")).count();
System.out.println(count);
Stream流,不能直接操作数据源如数组,集合中数据
Stream流的收集操作
对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中
//collect负责收集数据
//获取流中剩余数据,但他不负责创建容器,也不负责把数据添加到容器中
//collectors.tolist():在底层创建一个list集合,并把所有的数据添加到list集合中
List<Integer> list = list1 .stream().filter(number -. number %2 ==0).collect(collectors.toList());
System.out.println(list);
代码示例
public class CollectDemo {
public static void main(String[] args) {
//创建List集合对象
List<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
/*
//需求1:得到名字为3个字的流
Stream<String> listStream = list.stream().filter(s ‐> s.length() == 3);
//需求2:把使用Stream流操作完毕的数据收集到List集合中并遍历
List<String> names = listStream.collect(Collectors.toList());
for(String name : names) {
System.out.println(name);
}
*/
//创建Set集合对象
Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(20);
set.add(30);
set.add(33);
set.add(35);
/*
//需求3:得到年龄大于25的流
Stream<Integer> setStream = set.stream().filter(age ‐> age > 25);
//需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
Set<Integer> ages = setStream.collect(Collectors.toSet());
for(Integer age : ages) {
System.out.println(age);
}
*/
//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33", "柳岩,25"};
//需求5:得到字符串中年龄数据大于28的流
Stream<String> arrayStream = Stream.of(strArray).filter(s ‐>
Integer.parseInt(s.split(",")[1]) > 28);
//需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
Map<String, Integer> map = arrayStream.collect(Collectors.toMap(s ‐> s.split(",")
[0], s ‐> Integer.parseInt(s.split(",")[1])));
Set<String> keySet = map.keySet();
for (String key : keySet) {
Integer value = map.get(key);
System.out.println(key + "," + value);
}
}
}
/**
* Stream流的收集方法
*
* 创建一个ArrayList集合,并添加以下字符串。字符串中前面是姓名,后面是年龄
* "zhangsan,23"
* "lisi,24"
* "wangwu,25"
* 保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
*/
public class MyStream8 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("zhangsan,23");
list.add("lisi,24");
list.add("wangwu,25");
Map<String, Integer> map = list.stream().filter(
s -> {
String[] split = s.split(",");
int age = Integer.parseInt(split[1]);
return age >= 24;
}
// collect方法只能获取到流中剩余的每一个数据.
//在底层不能创建容器,也不能把数据添加到容器当中
//Collectors.toMap 创建一个map集合并将数据添加到集合当中
// s 依次表示流中的每一个数据
//第一个lambda表达式就是如何获取到Map中的键
//第二个lambda表达式就是如何获取Map中的值
).collect(Collectors.toMap(
s -> s.split(",")[0], //获取键
s -> Integer.parseInt(s.split(",")[1]) ));//获取值
System.out.println(map);
}
}
重写的hashCode()
意思是对于同一个类,他们的对象的哈希值是一样的,但是不同对象的hash值也可能是一样的,这种情况下会将值挂在原来哈希表存储位置的下边。
单列集合,双列集合,数组分别是如何获取Stream流对象的
1、java.util.Collection接口中加入了default方法stream()获取流对象,因此其所有实现类均可通过此方式获取流。
2、java.util.Map接口想要获取流,先通过keySet()、values()或entrySet()方法获取键、值或键值对的单列集合,再通过stream()获取流对象。
3、数组获取流,使用Stream接口中的的静态方法of(T...values)获取流。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String,String>>entryStream = map.entrySet().stream();
String[] array = {"东邪", "西毒", "南帝", "北丐", "中神通"};
Stream<String> stream = Stream.of(array);
}