一、Set集合
Set特点:无序(添加数据的顺序和获取出的数据顺序不一致),不重复,无索引
public class demo1 {
public static void main(String[] args) {
//1.创建一个集合
//HashSet特点:无序,不重复,无索引
//LinkedSet:有序的,根据谁先加入,输出顺序就是谁先输出
Set<String> s = new LinkedHashSet<>();
s.add("java");
s.add("python");
s.add("javascript");
s.add("java");
System.out.println(s);
//创建一个TreeSet:它是排序的(默认一定会排,升序排列),不重复,无索引
Set<Double> s2 = new TreeSet<>();
s2.add(7.0);
s2.add(2.5);
s2.add(3.7);
System.out.println(s2);
}
}
1.1HashSet
底层:基于哈希表(数组+链表+红黑树)实现的
HashSet集合元素的去重操作:需求:创建一个储存学生对象的集合,当学生对象成员变量值相同时,我们认为是同一个对象,要求只保留一个。
@Override
// s3.equals(s1) 方法的逻辑如下
//重写后的equals方法,保证了两个对象的内容一样,结果equals返回的一定是true
public boolean equals(Object o) {
//如果自己和自己比,直接return true
if (this == o) return true;
//如果o为空或者o不是学生类型,返回false
if (o == null || getClass() != o.getClass()) return false;
//比较两个对象的内容是否一样:
Student student = (Student) o;
return age == student.age && gender == student.gender && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
//重写后的方法保证了,如果对象内容一样,返回的哈希值也一样
return Objects.hash(name, age, gender);
}
@Override
public String toString() {
return "Student{" +
"name='" + this.name + '\'' +
", age=" + this.age +
", gender=" + this.gender +
'}' + "\n";
}
//掌握HashSet集合去重操作
public class demo2 {
public static void main(String[] args) {
Student s1 = new Student("john",15,'女');
Student s2 = new Student("Bob",24,'男');
Student s3 = new Student("Bob",24,'男');
Set<Student> s = new HashSet<>();
s.add(s1);
s.add(s2);
s.add(s3);
System.out.println(s);
/*
[Student{name='Bob', age=24, gender=男}
, Student{name='john', age=15, gender=女}
, Student{name='Bob', age=24, gender=男}
] 发现并没有去重,因为两个Bob的地址不一样,Hash值算出来也不一样!
*/
//如果希望Set集合认为两个内容一样的对象是重复的,必须重写对象的hashCode和equals方法!
//同样,右键-generate直接帮我们生成
}
1.2LinkedHashSet
有序(先加的在前面,后加的在后面),不重复,无索引
底层原理:基于哈希表(数组+链表+红黑树)实现。但是,它的每个元素都额外多了一个双链表的机制来记录他前后元素的位置,从头指针读到尾指针
1.3TreeSet
特点:不重复,无索引,可排序(默认升序排序,按照元素大小,从小到大排)
底层:基于红黑树实现
注意:
对于数值类型(Integer , Double),默认按照数值本身大小升序排列
对于字符串类型,默认按照首字符的编号升序排序
对于自定义类型,如Student对象,TreeSet无法直接排序,需要自定义排序规则
方案一:这个类去实现Comparable接口
public class Teacher implements Comparable<Teacher> {
public class Demo3_TreeSet {
public static void main(String[] args) {
Teacher t1 = new Teacher("alice",15,'女');
Teacher t2 = new Teacher("Bob" , 28 , '男');
Teacher t3 = new Teacher("张三" , 22 , '男');
TreeSet<Teacher> s = new TreeSet<>();
s.add(t1);
s.add(t2);
s.add(t3);
System.out.println(s);//报错,因为自定义对象无法直接排序,因为不知道大小比较的规则
//两种解决方法:1.让对象所属类去实现一个Comparable比较接口,重写compare方法,指定大小比较规则
//2.public TreeSet自带比较器Comparator对象,指定比较规则
}
}
//t2.compareTo(t1)
//t2 == this 方法中,this代表主调
//t2 == o;
//龟腚:如果你认为左边大于右边,返回一个正整数;反之,负整数
@Override
public int compareTo(Teacher o){
//按照年龄升序
if(this.age > o.age) return 1;
else if(this.age < o.age) return -1;
else return 0;
}
重写后,可以按找年龄大小排序了
方案二:TreeSet自带Comparator,我们调用它,可以排序。(优先使用comparator的规则)
//方案2:
TreeSet<Teacher> s2 = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
return o2.getAge() - o1.getAge(); // 降序排列
//注意,这里集合中方法重写是降序排列,Teacher类定义的是升序排列
//调用时优先采用集合的降序,如果集合提供了Comparator的话
}
});
小结:Arraylist和HashSet更常用
二、Map集合
2.1Map的特点
Map又叫键值对集合,建不能重复,值无所谓,一个键对应一个值。
public class hashMap {
public static void main(String[] args) {
Map<String,Integer> m = new HashMap<>(); // 这是一行经典代码
m.put("trump", 34);
m.put("john", 50);
m.put("ronnie", 50);
m.put(null, null);
System.out.println(m);
//特点:无序,不重复,无索引。
}
}
2.2 Map的常用方法
Map集合是所有Map集合的父类,学完他的方法,它的子类也继承了这些方法。
2.3Map的遍历方式
1.键找值
先获取Map集合的全部键,在通过遍历键来找值
public Set<k> key set() 获取所有键的集合
public V get(object key) 根据键获取其对应的键
public class MapTraversal {
public static void main(String[] args) {
Map<String,Integer> m = new HashMap<>(); // 这是一行经典代码
m.put("trump", 34);
m.put("john", 50);
m.put("ronnie", 51);
m.put(null, null);//{null=null, ronnie=50, trump=34, john=50}
//1.提取map集合的全部键,用一个Set集合装
Set<String> keys = m.keySet();
System.out.println(keys);
//2.遍历set集合的每个键,去找值
for(String key : keys){
System.out.println("key" + "=" + m.get(key));
}
}
2.键值对
把键值对看作一个整体进行遍历。(难度较大)
使用map提供的entrySet方法,获取所有键值对
Set<Map.Entry<K,V>>entrySet()
entryset是一个实现类对象,用来封装键值对,作为一个整体。
//1.把map集合转化成set集合,里面的元素是键值对类型(map.entryset<K键的类型 V值得类型>)
Set<Map.Entry<String,Integer>> entries = m.entrySet();
for(Map.Entry<String , Integer> entry : entries) {
System.out.println(entry);
}
3.lambda
JDK8之后的新技术,非常的简单,需要用到Map的方法
default void forEach(BiConsumer<? super K , ? super V> action)
结合lambda遍历map集合
这个方法需要提供一个匿名内部类对象,可以直接简化成lambda表达式。
public class MapTraversal3_lambda {
public static void main(String[] args) {
Map<String,Integer> m = new HashMap<>(); // 这是一行经典代码
m.put("trump", 34);
m.put("john", 50);
m.put("ronnie", 51);
m.put("david", 23);
m.put(null, null);//{null=null, ronnie=50, trump=34, john=50}
System.out.println(m);
//直接调用map集合的forEach方法完成遍历
// m.forEach(new BiConsumer<String, Integer>() {
// @Override
// public void accept(String key, Integer value) {
// System.out.println(key + ":" + value);
// }
// });
//上述代码可以简化
m.forEach((k,v)->System.out.println(k + "->" + v));
2.4Map集合的实现类——由键决定特点
1.HashMap(用的最多)
无序,不重,无索引!
事实上,原来的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
2.LinkedHashMap
有序,不重,无索引
事实上,原来的LinkedHashSet底层就是LinkedHashMap。它们都是基于哈希表实现的,只是每个键值对额外多加了一个双向链表。
3.TreeMap
按照大小默认升序排列,不重复,无索引。
TreeMap跟TreeSet底层一样,都是基于红黑树实现。
三、Stream流
3.1认识stream流
stream流是JDK8的新增的api,可以用于操作集合或数组的数据。
为什么要用Stream流?
结合了大量的lambda风格的语法,功能强大,性能高效,代码简洁,可读性好。
public class test1 {
public static void main(String[] args) {
List<String> ls = new ArrayList<String>();
ls.add("张无忌");
ls.add("周芷若");
ls.add("周传雄");
ls.add("张学友");
ls.add("张敬轩");
System.out.println(ls); //需求:把集合中所有以张开头,且是三个字的元素存到一个新的集合
//传统方法完成需求
// List<String> new_ls = new ArrayList<>();
// for(String s : ls){
// if(s.startsWith("张") && s.length() == 3){
// new_ls.add(s);
// }
// }
// System.out.println(new_ls);
//使用Stream流,调用stream方法,把它想象成一个传送带,集合中的元素被扔到这个传送带上了
List<String> ls2 = ls.stream().filter(s ->s.startsWith("张")).filter(s -> s.length()==3).collect(Collectors.toList());
System.out.println(ls2);
stream的使用步骤:
1.提供数据源(集合、数组。。)
2.获取数据流,stream流代表一条流水线,并能与数据源简例连接
3.调用流水线的各种方法
4.获取处理的结果,遍历统计收集到一个新集合中返回。
3.2获取stream流
public class demo2 {
public static void main(String[] args) {
//1.获取集合的stream流:调用集合的stream方法
Collection<String> list = new ArrayList<String>();
Stream<String> s1 = list.stream();
//2.Map集合,如何获取?
Map<String, Integer> map = new HashMap<String, Integer>();
//获取键流,先调用map的keySet方法,在调用.stream方法。龟腚动作
Stream<String> stream = map.keySet().stream();
//获取值流:先调用map的values方法,在调用.stream方法。龟腚动作
Stream<Integer> stream1 = map.values().stream();
//也可以获取键值对流,先调用map的entrySet方法,在调用.stream方法。龟腚动作
Stream<Map.Entry<String, Integer>> stream2 = map.entrySet().stream();
//3.获取数组的stream流:两种方法Arrays.stream()或者Stream.of()
String[] names = {"jhon" , "ronnie" ,"jack"};
Stream<String> stream3 = Arrays.stream(names);
Stream<String> stream4 = Stream.of(names);
//Stream.of()里面接的是可变参数,事实上可以直接这么写
Stream<String> stream5 = Stream.of("david","jay");
System.out.println(stream5.count());
3.3stream流的常用操作(中间方法)
中间方法指的是调用完成后会返回新的stream流,可以继续使用,支持链式编程。
//掌握stream流提供的常用中间方法,对流上的数据进行处理,返回新的流
public class demo3 {
public static void main(String[] args) {
List<String> ls = new ArrayList<String>();
ls.add("张无忌");
ls.add("周芷若");
ls.add("周传雄");
ls.add("张学友");
ls.add("张敬轩");
System.out.println(ls);
//1.过滤方法
ls.stream().filter(s ->s.startsWith("张")).forEach(System.out::println);
//2.排序方法,默认升序,若想要降序,使用它重载的方法
List<Double> scores = new ArrayList<Double>();
scores.add(1.0);
scores.add(6.0);
scores.add(3.14);
scores.add(1.0);
System.out.println(scores);
//scores.stream().sorted().forEach(System.out::println);
scores.stream().sorted((s1 , s2) ->Double.compare(s2,s1)).forEach(System.out::println);//降序
//去重
System.out.println("=============");
scores.stream().distinct().forEach(System.out::println);
//如果需要实现自定义对象的去重,需要重写hasCode和equals方法
System.out.println("==============================");
//加工方法(映射方法):把流上原来的数据拿出来加工,变成新数据,又放上去
scores.stream().map(s->"加十分后:" + (s + 10)).forEach(System.out::println);
//合并流:假设有s1 s2两个流,使用stream.concat()方法,合并之
//Stram<Object> s3 = Stream.concat(s1,s2)
}
}
3.4终结方法、收集结果
终结方法是指,调用完之后,不会返回新的stream流了,无法继续使用。
//掌握stream流的终结操作
public class demo4_stream_final {
public static void main(String[] args) {
List<Teacher> teachers = new ArrayList<Teacher>();
teachers.add(new Teacher("张三",50,'男'));
teachers.add(new Teacher("John",30,'男'));
teachers.add(new Teacher("Alice",35,'女'));
teachers.stream().filter(t-> t.getGender()=='男').forEach(System.out::println);
System.out.println("===============");
System.out.println(teachers.stream().filter(t -> t.getAge() > 31).count());
System.out.println("===============");
Optional<Teacher> max = teachers.stream().max((t1, t2) -> Double.compare(t1.getAge(), t2.getAge()));
Teacher teacher = max.get();//获取年龄最大的老师对象
System.out.println(teacher);
}
收集stream流