java基础--内部类、集合、lambda

1.内部类

分类

  • 成员内部类: 一般多用于框架, 是用来对类的功能进行增强的
  • 局部内部类: 这个是我们常用的类型(它属于局部内部类的一种)

局部位置: 方法中, 或者方法的形参列表。

1.1匿名对象

概述: 没有名字的对象, 就叫匿名对象。

特点:用完以后就会变成垃圾对象, 由GC在不确定的时间回收。

eg: new Cat().eat();

适用于只使用一次对象的情况。

1.2匿名内部类

概述:匿名内部类就是没有名字的局部内部类。

格式

new 类名或者接口名() {

​ //重写类或者接口中所有的抽象方法.

​ };

本质:就是一个继承了类或者实现了接口的匿名的子类对象。

eg:new Animal() {

​ //重写类或者接口中所有的抽象方法.

​ @Override

public void eat() {

​ System.out.println(“我是通过匿名内部类的形式创建的 Animal类的子类对象”);

​ } }.eat();

使用场景

  • 当对对象方法(也叫: 成员方法)仅调用一次的时候。
  • 匿名内部类可以作为方法的实参进行传递。

技巧:当抽象类或者接口中的抽象方法不超过3个的时候(一般是只有1个), 就可以考虑使用匿名内部类.

2.集合

概述:集合是用来存储多个同类型数据的容器, 它的长度是可以变化的

体系图

在这里插入图片描述

2.1 Collection集合

概述:Collection集合是单例集合的顶层接口, 里边定义了所有单列集合都共有的内容

格式

因为Collection是接口, 所以不能直接通过new关键字来创建它的对象,可以通过多态的方式, 创建其子类对象, 从而实现创建Collection接口对象。

Collection list= new ArrayList();

集合后边的<数据类型>是泛型的意思, 泛型是用来限定集合中存储元素的数据类型的,泛型一般只结合集合使用。泛型除了有基本数据类型,也可以是自定数据类型。

成员方法

  • public boolean add(E e) 添加元素.
  • public boolean remove(Object obj) 从集合中移除指定的元素.
  • public void clear() 清空集合对象
  • public boolean contains(Objectobj) 判断集合中是否包含指定的元素
  • public boolean isEmpty() 判断集合是否为空
  • public int size() 获取集合的长度, 即集合中元素的个数

集合遍历

根据集合对象获取其对应的迭代器对象,通过Collection#iterator()方法实现.

判断迭代器中是否有下一个元素,通过Iterator#hasNext()方法实现.

如果有, 就获取该元素,通过Iterator#next()方法实现.

Iterator it = coll.iterator();

​ //3.2 判断迭代器中是否有下一个元素.

​ while (it.hasNext()) {

​ //3.3 有就获取, 然后输出.

​ String s = it.next();

​ System.out.println(s); }

简化版:while (it.hasNext())
System.out.println(it.next());

2.2 List集合

概述:有序集合(也称为序列), 该界面的用户可以精确控制列表中每个元素的插入位置。且用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。

特点

  • 有序: 指的是元素的存储顺序和取出顺序是一致的.
  • 可重复: 指的是List集合可以存储重复的元素.
  • 元素有索引: 指的是List集合中每个元素都是由索引的, 且索引是从0开始的.

List特有的成员方法

  • public void add(int index, Eelement) 解释: 在集合的指定位置(索引), 插入指定的元素, 索引越界会报IndexOutOfBoundsException异常.
  • public E remove(int index) 解释: 删除指定索引处的元素, 并返回被删除的元素, 索引越界会报IndexOutOfBoundsException异常.
  • public E set(int index, Eelement) 解释: 修改指定索引处的元素为指定的值, 并返回修改前的元素, 索引越界会报IndexOutOfBoundsException异常.
  • public E get(int index) 解释: 根据索引, 获取其对应的元素, 索引越界会报IndexOutOfBoundsException异常.
  • public ListIterator listIterator(); //根据集合对象获取其对应的列表迭代器对象

List集合遍历

使用list特有的size()、get()方法来遍历集合。

for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}

列表迭代器指的是**ListIterator接口, 它是List集合特有的迭代器.**该迭代器继承了Iterator迭代器,所以, 我们可以直接使用。

列表迭代器成员方法

  • hasNext(); 判断集合中是否有下一个元素.
  • next(); 有就获取集合中的下一个元素.
  • hasPrevious(); 判断集合中是否有上一个元素.
  • previous(); 有就获取集合中的上一个元素.

有了这些方法List集合就可以正向遍历也可以逆向遍历,但是逆向遍历前必须进行一次正向遍历。

并发修改异常

:当使用普通迭代器(Iterator)遍历集合的同时, 又往集合中添加了元素, 就会报并发修改异常

:迭代器是依赖于集合而存在, 当判断成功后, 集合中添加了新的元素, 而迭代器不知道, 所以就报错了

方式1:通过 列表迭代器(ListIterator)来解决。

要求:必须使用列表迭代器中的添加元素的方法. ListIterator#add();这种方式添加的元素是在刚才迭代到的元素后边。

方式2:通过for循环 + size()方法来解决。

要求:这种方式添加的元素是在集合的最后位置添加的。

方式3:通过CopyOnWriteArrayList集合解决。

2.3 增强for循环

概述:用来简化数组和Collection集合的遍历的,它底层是一个迭代器。

格式:for(数据类型 元素名 : 要遍历的集合或者数组) {
//元素名: 表示的就是集合/数组中的每一个元素}

注意事项:增强for的,并且增强for来遍历的数组或者集合不能为null。

通过测试增强for循环是否会发生并发修改异常,可得增强for循环底层是普通迭代器iterator。

2.4 List集合子类

List集合是一个接口, 它的常用子类有两个, 分别是ArrayList,LinkedList.

ArrayList集合的特点: 底层数据结构是数组, 查询和修改快,增删慢.

LinkedList集合的特点: 底层数据结构是链表, 查询和修改慢, 增删快.

LinkedList的特有方法

  • public void addFirst(E e) 往列表的开头插入指定的元素
  • public void addLast(E e) 往列表的末尾插入指定的元素
  • public E removeFirst() 删除列表中的第一个元素, 并返回被删除的元素
  • public E removeLast() 删除列表中的最后一个元素, 并返回被删除的元素.
  • public E getFirst() 返回列表的第一个元素
  • public E getLast() 返回列表的最后一个元素
2.5 Set集合

概述:Set集合是Collection集合的子体系, 它的元素特点是无序, 唯一.

特点

  • Set集合是一个接口, 所以不能通过new的方式直接创建它的对象。
  • Set集合中没有带索引的方法, 所以不能通过普通for循环遍历.。
  • Set集合的常用子类主要有两个, 分别是HashSet集合和TreeSet集合。

set集合遍历

  • 方式1:普通迭代器iterator遍历
  • 方式2:增强for循环遍历
  • 方式3:转数组遍历

Object[] objs = hs.toArray();
for (Object obj : objs) {
System.out.println(obj); }

哈希值指的是JDK根据对象的地址, 或者字符串, 或者数字算出来的int类型的数值。

获取哈希值方法:public int hashCode(); //根据对象, 获取其对应的哈希值.

注意:实际开发中, 我们认为, 如果同一个类的两个对象, 各个属性都相同, 那么它们就是同一个对象。但是object类的hashcode()方法是按照地址值算的。只要涉及到hashCode()方法了, 子类一般都会重写hashCode()方法, 采用属性值计算哈希值。同一对象哈希值肯定相同, 不同对象哈希值一般不同

2.6 Hashset集合

特点:无序, 唯一, 元素无索引, 它的底层数据结构是: 哈希表(数组 + 链表)。

Hashset集合遍历

  • 方式1:普通迭代器iterator遍历
  • 方式2:增强for循环遍历

hashset唯一性原理

//p.hash: 集合中已经存在的元素的哈希值. hash: 要添加的元素的哈希值
//k: 就是集合中的元素对象"hello", “world” key: 要添加的元素 “world”

if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) {
//走这里, 说明是同一个元素, 不添加元素.
} else {
//走这里, 说明不是同一个元素, 就添加元素.
}

四次判断: ( ?? && ( ?? || (?? && ?? )) )

  • 判断两个对象(集合中已经存在的某个对象 和 要添加的元素)的哈希值是否相同.

哈希值相同: 说明可能是同一个元素, 程序继续往下运行.
哈希值不同: 说明不是同一个元素, 就添加.

  • 比较两个对象的地址值是否相同

地址值相同: 说明是同一个元素, 不添加.
地址值不同: 说明可能不是同一个元素, 程序继续往下执行

  • 判断要添加的元素是否为null

如果为null: 不是同一个元素, 就添加.
如果不为null: 说明可能是同一个元素, 程序继续往下运行.

  • 比较两个对象的各个属性值是否相同.

如果相同: 说明是同一个元素, 不添加元素.
如果不同: 说明不是同一个元素, 就添加.

:为什么要设计的这么复杂呢? 直接通过equals()比较两个对象的属性值不就好了吗?

:确实可以直接比较equals()方法这样设计, 但是效率比较低. 程序之所以设计的这么繁琐, 就是为了降低调用equals()方法的次数,从而实现"节约资源, 提高效率"

结论:HashSet保证元素的唯一性, 依赖hashCode()和equals()方法

hash表:

JDK8以前,底层采用数组 + 链表的形式实现, 可以理解为: 一个元素为链表的数组,但是这样表的长度比较长,

JDK8以后,底层实现了优化. 存储的时候, 会将上述的哈希值和16进行取余操作, 然后根据余数值进行存储。

在这里插入图片描述

2.7 LinkedHashSet集合

概述:LinkedHashSet集合是HashSet集合的子类。

特点

  • 底层的数据结构是通过哈希表 + 链表实现的, 具有可预测的迭代次序。
  • 由链表保证元素有序, 也就是说元素的存取顺序是一致的。
  • 由哈希表保证元素唯一, 也就是说没有重复的元素。
2.8 可变参数

概述:可变参数又称参数个数可变,它用作方法的形参出现,那么方法参数个数就是可变的了。

格式:public static int getSum(int… a)即: 在数据类型的后边加上 3个点

注意

  • 可变参数的底层就是一个数组
  • 方法的形参列表有且只能有一个可变参数, 且可变参数要放最后
2.9 Map

概述:Map集合是双列集合的顶层接口,它是用来存储键值对对象,其中键具有唯一性, 而值是可以重复的。

注意:Map是一个接口,不能直接new,可以通过多态的方式创建。常用子类是HashMap、TreeMap。

格式:public interface Map<K, V>

成员方法

方法名说明
V put(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数

put()方法返回值:public V put(K key, V value) 添加元素到双列集合, key不存在就直接添加, 并返回null,key存在, 会用新值覆盖旧值。 并返回旧值。

方法名说明
V get(Object key)根据键获取值
Set keySet()获取所有键的集合
Collection values()获取所有值的集合
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合

keySet()方法返回值是集合是key值,具有唯一性,所以是set。

values()方法返回值是集合value值,可以重复,所以是collection。

Map集合遍历

方式1:根据键的集合获取其对应的值。

增强for循环

//获取所有key值

Set keys = hm.keySet();

for (String key : keys) {

​ //3.3 根据键获取其对应的值.

​ String value = hm.get(key);

​ System.out.println(key + “…” + value); }

普通迭代器

Set keys = hm.keySet();

while (it.hasNext()) {

​ String key = it.next();

​ System.out.println(key + “…” + hm.get(key)); }

方式2:根据键值对对象获取其对应的键和值,使用Map.Entry中的方法: getKey(), getValue()。

增强for循环

​ // 获取到所有的 键值对对象 的集合.

​ Set<Map.Entry<String, String>> entrys = hm.entrySet(); //需要导入: import java.util.Map;

​ for (Map.Entry<String, String> entry : entrys) {

​ // 根据 键值对对象 获取其对应的键和值.

​ System.out.println(entry.getKey() + “…” + entry.getValue());
}

普通迭代器

​ Set<Map.Entry<String, String>> entrys = hm.entrySet(); //需要导入: import java.util.Map;

​ // 获取到所有的 键值对对象 的集合.

​ Iterator<Map.Entry<String, String>> it = entrys.iterator();

​ // 遍历, 获取到每一个 键值对对象.

​ while (it.hasNext()) {

​ Map.Entry<String, String> entry = it.next();

​ // 根据 键值对对象 获取其对应的键和值.

​ System.out.println(entry.getKey() + “…” + entry.getValue()); }

综合案例(统计字符串字符出现次数):

public class Demo05 {

​ public static void main(String[] args) {
/*1.键盘录入一个字符串,要求统计字符串中每个字符出现的次数。

​ 2.举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)” */

​ //1. 定义Map集合, 字符做键, 该字符对应的次数作为值. 即: HashMap<Character, Integer> a:3, b:1

​ Map<Character, Integer> map = new HashMap<>();

​ //2. 提示录入, 然后遍历, 获取到每一个字符.

​ System.out.println("请录入一个字符串: ");

​ for (char ch : new Scanner(System.in).nextLine().toCharArray())

​ //6. 判断该字符在双列集合中是否存在.

​ map.put(ch, !map.containsKey(ch) ? 1 : map.get(ch) + 1);

​ //已知格式: a:3, b:1 目标格式: a(5)b(4)c(3)d(2)e(1)

​ //7. 走到这里, Map集合记录的就是我们要的结果, 将其拼接成字符串.

​ StringBuilder sb = new StringBuilder();

​ for (Character key : map.keySet())

​ sb.append(key).append(“(”).append( map.get(key)).append(“)”);

​ //8. 打印结果.

​ System.out.println(sb);}}

2.10 工具类Collections集合

概述:Collections类是针对集合操作的工具类

方法:

方法名说明
public static void sort(List list)将指定的列表按升序排序
public static void reverse(List<?> list)反转指定列表中元素的顺序
public static void shuffle(List<?> list)使用默认的随机源随机排列指定的列表

工具类使用方法:Collections.sort(list);

3.lambda

格式:(形式参数) -> {代码块}

解释

  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
  • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
  • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
  • 组成Lambda表达式的三要素:形式参数,箭头,代码块

前提:有一个接口, 且接口中有且仅有一个抽象方法.

省略规则

  • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,那么小括号可以省略
  • 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字

注意事项

使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法

必须有上下文环境,才能推导出Lambda对应的接口,要么直接当接口的子类来使用,要么当方法的实参进行传递。

lambda和匿名内部类的区别

  • 所需类型不同
  1. 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
  2. Lambda表达式:只能是接口
  • 使用限制不同
  1. 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
  2. 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
  • 实现原理不同
  1. 匿名内部类:编译之后,产生一个单独的.class字节码文件
  2. Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值