IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 大数据进阶20-泛型、增强for循环,静态导入与可变参数以及Set集合 -> 正文阅读

[大数据]大数据进阶20-泛型、增强for循环,静态导入与可变参数以及Set集合

这一块的主要内容是有关泛型、增强for循环以及Set集合的内容

泛型

泛型的引入

我们写下面这样的代码,发现是有一些小问题的,因为我们同时存储了String类型和Integer类型的数据,但都给它转成了String类型,在运行的时候就会报错,相反,数组在存储数据的时候,只能存储一个类型的数据,这也算是数组的一个优点,那,在集合中有没有这样的做法,在创建的时候就明确了元素的数据类型,这样我们在插入数据或者是向下转型的时候,直接在编译时期报错,在集合中,这种设计我们叫做:泛型。

public static void main(String[] args) {

        ArrayList list = new ArrayList();

        list.add("hello");
        list.add("world");
        list.add(100);

        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            String next1 = (String) next;
            System.out.println(next1);
        }
        
    }

泛型的介绍

泛型:一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的类型。参数化类型,把类型当作参数一样传递
格式:
<数据类型>
注意:此处的数据类型只能是引用类型

泛型的好处

  • 把运行时期的问题提前到了编译时期
  • 避免了强制类型转换
  • 优化程序,消除黄色警告线

泛型的举例应用

泛型存储String类型数据举例

   public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<String>();

        list.add("hello");
        list.add("world");
//        list.add(100);直接就报错了

        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String r = iterator.next();
            System.out.println(r);
        }
    }
}
//hello
//world

泛型存储自定义对象举例

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;
    }
}

public class ArrayListDemo2 {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<Student>();

        Student s1 = new Student("zhangsan", 21);
        Student s2 = new Student("lisi", 22);
        Student s3 = new Student("wangwu", 23);

        list.add(s1);
        list.add(s2);
        list.add(s3);

        Iterator<Student> iterator = list.iterator();
        while (iterator.hasNext()){
            Student next = iterator.next();
            System.out.println(next.getName()+"---"+next.getAge());
        }
    }
}

泛型类

泛型类:把泛型定义在类上

这里仅仅表示的是一种参数类型,参数类型是一种变量,既然是变量,就满足我们变量的命名规则,可以是任何符合标识符的名字


public class GenericTool<T> {
    private T obj;

    public T getObj(){
        return obj;
    }

    public void setObj(T obj){
        this.obj = obj;
    }

}


public static void main(String[] args) {

        GenericTool genericTool = new GenericTool();
        genericTool.setObj(new String("zhangsan"));
        Object obj = genericTool.getObj();
        String obj1 = (String) obj;
        System.out.println(obj1);

        genericTool.setObj(new String("lisi"));
        Object obj2 = genericTool.getObj();
        String obj21 = (String) obj2;
        System.out.println(obj21);

        GenericTool<String> stringGenericTool = new GenericTool<String>();
        stringGenericTool.setObj("wangwu");
        String obj3 = stringGenericTool.getObj();
        System.out.println(obj3);
    }
}
//zhangsan
//lisi
//wangwu

泛型方法

public class GenericTool2 {
    public <T> void show(T t){
        System.out.println(t);
    }
}


public static void main(String[] args) {
        GenericTool2 genericTool2 = new GenericTool2();
        genericTool2.show("hello");
        genericTool2.show("world");
        genericTool2.show("java");
    }
}
//hello
//world
//java


泛型接口

public interface GenericTool3<T>{
    public abstract void show(T t);
}


public class GenericTool3Impl<T> implements GenericTool3<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}


public static void main(String[] args) {
        //第一种情况测试
        GenericTool3Impl<String> tool3 = new GenericTool3Impl<>();
        tool3.show("hello");

        //第二种情况测试
        GenericTool3Impl<Integer> tool31 = new GenericTool3Impl<>();
        tool31.show(100);
    }
}
//hello
//100

泛型高级


/**
 *
 *  泛型高级用法(通配符)
 *  泛型通配符<?>
 *          任意类型,如果没有明确,那么就是Object以及任意的Java类了
 * ? extends E
 *          向下限定,E及其子类
 * ? super E
 *          向上限定,E及其父类
 */
public class GenericDemo2 {
    public static void main(String[] args) {
        //如果用一个的时候,泛型如果明确了类型的时候,前后必须要写一致
        ArrayList<Object> a1 = new ArrayList<Object>();
//        ArrayList<Object> a2 = new ArrayList<Animal>();
//        ArrayList<Object> a3 = new ArrayList<Dog>();
//        ArrayList<Object> a4 = new ArrayList<Cat>();

        //型通配符<?>
        //  任意类型,如果没有明确,那么就是Object以及任意的Java类了
        //还没有明确类型
//        ArrayList<?> list1 = new ArrayList<Object>();
//        ArrayList<?> list2 = new ArrayList<Dog>();
//        ArrayList<?> list3 = new ArrayList<Cat>();
//        ArrayList<?> list4 = new ArrayList<>();


        //? extends E
        //   向下限定,E及其子类
        ArrayList<? extends Animal> o1 = new ArrayList<Animal>();
        ArrayList<? extends Animal> o2 = new ArrayList<Dog>();
        ArrayList<? extends Animal> o3 = new ArrayList<Cat>();
//        ArrayList<? extends Animal> o4 = new ArrayList<Object>();

        //? super E
        //  向上限定,E及其父类
        ArrayList<? super Animal> alist1 = new ArrayList<Object>();
//        ArrayList<? super Animal> alist2 = new ArrayList<Dog>();
//        ArrayList<? super Animal> alist3 = new ArrayList<Cat>();
        ArrayList<? super Animal> alist4 = new ArrayList<Animal>();


    }
}



public class Animal {
}


public class Cat extends Animal {
}


public class Dog extends Animal {
}


增强for循环

什么是增强for循环

  • JDK1.5之后出现了一些新内容:
    • 自动装箱和拆箱
    • 泛型
    • 增强for循环
  • 增强for循环:是for循环的一种,简化数组和Collection集合的遍历
    for(元素数据类型 变量:数组或者Collection集合){
    使用变量即可,该变量就是元素
    }
  • 好处:简化了数组和Collection集合的遍历

增强for循环举例

注意:增强for可多次使用,迭代器只能使用一次,生命周期就结束了(因为迭代器只遍历一次,指针指到末尾就结束了)

    public static void main(String[] args) {
        //定义一个数组
        int []arr = new int[]{1,2,3,4,5,6};
        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }

        //增强for循环改进
        //Java中能用增强for循环替代的,就使用增强for循环替代,减少黄色警告线
        for(int i:arr){
            System.out.println(i);
        }

        //定义一个集合对象
        ArrayList<String> strings = new ArrayList<>();
        strings.add("hello");
        strings.add("world");
        strings.add("java");
        for(String s:strings){
            System.out.println(s);
        }

        //这一步只是相较于刚刚上一步进行了一个改进,加了一个判断
        List<String> list1 = null;
        if(!(list1 == null)){
            for (String s1:list1) {
                System.out.println(s1);
            }
        }
        //其实增强for循环就是用来替代迭代器的
        //如何验证它是用来替代迭代器的呢
        //之前在看迭代器的时候,遇到了一个并发修改异常
        //ConcurrentModificationException
        for(String s:strings){
            if("java".equals(s)){
                strings.add("spark");
            }
        }
    }

存储自定义对象,加入泛型,并且使用增强for循环遍历

public static void main(String[] args) {

//        ArrayList<String> strings = new ArrayList<>();
//        strings.add("hello");
//        strings.add("world");
//        strings.add("java");
//
//        for(String s:strings){
//            System.out.println(s);
//        }
//
//        //转换成迭代器遍历,同一个迭代器只能使用一次,因为遍历一次,指针指到末尾
//        Iterator<String> iterator = strings.iterator();
//        while (iterator.hasNext()){
//            String next = iterator.next();
//            System.out.println(next);
//        }
//        while (iterator.hasNext()){
//            String next = iterator.next();
//            System.out.println(next);
//        }

        ArrayList<Student1> list = new ArrayList<Student1>();

        Student1 s1 = new Student1("zhangsan", 21);
        Student1 s2 = new Student1("lisi", 22);
        Student1 s3 = new Student1("wangwu", 23);

        list.add(s1);
        list.add(s2);
        list.add(s3);

        for(Student1 s:list){
            System.out.println(s.getName()+"---"+s.getAge());
        }
        
//zhangsan---21
//lisi---22
//wangwu---23

静态导入

什么是静态导入

import static 包名…类名.方法名;(可以直接导入到方法的级别)

注意

  • 方法必须是静态
  • 如果本类中有其他与静态导入的方法重名的,优先使用本类的方法
  • 如果导入的时候有的方法重名了,怎么办,最好加前缀,规定必须加,这个意义不大,看懂即可
public static void main(String[] args) {

        System.out.println(Math.abs(-100));
        System.out.println(Math.pow(2,3));
        System.out.println(Math.max(100,200));

        //有没有什么办法可以不去写类名,直接写方法名
        //在开头导入:import static java.lang.Math.abs;
        System.out.println(abs(-900));

        FunDemo.show("hello");
        com.bigdata.shujia19.FunDemo.show(12.34);
    }
}
//100
//8.0
//200
//900
//hello
//12.34

可变参数

什么是可变参数,以及可变参数的注意点

  • 可变参数:定义方法的时候,方法的参数可以有多个
  • 修饰符 返回值类型 方法名(数据类型…变量名){}
  • 注意:这里变量,其实就是把多个参数,组成一个数组,给它起个名字,这个变量就是数组名

可变参数代码举例:

  public static void main(String[] args) {
        //两个数据求和
        int a = 10;
        int b = 20;
        int result1 = sum(a,b);
        System.out.println(result1);

        //三个数据求和
        int c = 30;
        int result2 = sum1(a,b,c);
        System.out.println(result2);

        //写一个求和需求,但是也不知道有多少个数据要求和,但是要求是在
        //调用方法的时候就必须弄清楚
        //怎么办,为了解决,Java提供了一个技术:可变参数

        int res4 = sum(a,b,c,40,50,20,304,70);
        System.out.println(res4);
    }

    public static int sum(int a,int b){
        return a+b;
    }
    public static int sum1(int a,int b,int c){
        return a+b+c;
    }
    public static int sum(int...arr){
        int sum = 0;
        for(int i:arr){
            sum += i;
        }
        return sum;
    }
}
//30
//60
//544

Set接口

Set接口的引入

Collection接口下有List和Set接口

  • List 元素有序(存储顺序和取出顺序一致,可以重复)
  • Set 无序(存储顺序和取出顺序不一致,元素不唯一)

HashSet:底层是hash表,线程不安全,效率高,有时候,给的顺序正好好存储的顺序一致,但这并不代表有序,可以多试试看,元素唯一,无序

    public static void main(String[] args) {
        HashSet<String> hashSet = new HashSet<>();

        //创建字符串对象并添加到集合中
        hashSet.add("hello");
        hashSet.add("world");
        hashSet.add("java");
        hashSet.add("hello");
        hashSet.add("world");

        for(String s:hashSet){
            System.out.println(s);
        }
    }
}
//world
//java
//hello

HashSet类

HashSet类概述

  • 不保证set的迭代顺序
  • 特别是不保证该顺序恒久不变
public static void main(String[] args) {
        //我们在做字符串存储的过程中,发现,HashSet存储数据确实是无序的,唯一的
        /*
        但为什么存储字符串的时候,相同的内容只存储一个呢
        我们去看源码

        观察源码我们发现,先看HashCode()值是否相同
            如果相同,就继续判断equals()方法
                返回的内容如果是true,说明元素内容一样,重复了,就不添加
                返回的内容如果是false,说明元素内容不重复,就添加到集合中
            如果不同,就直接加入到集合中
        如果一个类中,没有重写这两个方法,调用的就是父类Object中的方法
        为什么String对象,也可以去重,说明,String类中,重写了HashCode()和equasl()方法
        所以就可以把相同字符串的内容去掉,只留下一个
         */
        HashSet<String> hashSet = new HashSet<String>();
        //字符串添加到集合中
        hashSet.add("hello");
        hashSet.add("world");
        hashSet.add("java");
        hashSet.add("hello");
        hashSet.add("world");
        //增强for循环遍历集合
        for(String s:hashSet){
            System.out.println(s);
        }
    }

HashSet如何保证元素的唯一性

  • 底层数据结构是哈希表(元素是链表的数组)
  • 哈希表依赖于哈希值存储
  • 添加功能底层依赖的两个方法
    • int hashCode()
    • boolean equals(Object obj)
  public static void main(String[] args) {
        /*
        定义一个HashSet集合存储自定义对象,并保证元素的唯一性
        要求:如果两个对象的成员变量的值相同,说明是同一个元素

        按照需求我们写完了程序,但是并没有去重,所以这不是符合我们要求的
        又因为,我们知道了HashSet底层是依赖HashCode()和equals()方法的
        又因为学生类中没有重写,比较的是Object中的方法,默认比较的是地址值,
        每个学生都是new出来的,所以地址值都不一样,所以就都插进去了

        我们现在要去重写HashCode()和equals()方法,不需要我们自己手动写,自动生成即可
         */

        HashSet<Student> list = new HashSet<Student>();

        Student s1 = new Student("zhangsan", 21);
        Student s2 = new Student("lisi", 22);
        Student s3 = new Student("wangwu", 23);

        list.add(s1);
        list.add(s2);
        list.add(s3);

        for(Student s:list){
            System.out.println(s.getName()+"---"+s.getAge());
        }
    }
}
//wangwu---23
//zhangsan---21
//lisi---22

HashSet中add方法的源码

public interface Collection<E> extends Iterable<E>{
    ...
}

public interface Set<E> extends Collection<E>{
    ...
}

public class HashSet<E> implements Set<E>{
    //由哈希表(实际为HashMap实例)支持
    private transient HashMap<E,Object> map;

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    static final int hash(Object key) { //key -- e -- 要插入的元素
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    public V put(K key, V value) { //key -- e -- 要插入的元素
        return putVal(hash(key), key, value, false, true);
    }


    //HashSet中的add()方法,最终我们发现调用的是HashMap中的putVal()方法
    //hash的值与元素的HashCode()方法有关
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        //哈希表存储的是一个个的节点数组
        Node<K,V>[] tab; Node<K,V> p; int n, i;

        //判断哈希表是否初始化,如果没有初始化就进行初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

        //根据元素对象的哈希值计算出存储位置,如果该元素的位置是null就代表,该位置没有
        //元素,就创建新的节点,就存储元素
        //通过源码我们发现,HashSet的add()方法一定和HashCode()方法有关系
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            
            /*
                存入的元素和以前的元素的哈希值进行比较
                    如果哈希值不同,继续向下执行,把元素添加到集合
                    如果哈希值一样,会调用对象的equals()方法进行比较
                        如果返回的是false,会继续向下执行,然后将元素添加到集合中
                        如果返回的是true,说明元素内容一样,重复了,就不存储
            */
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
} 

HashSet中add自定义对象存储图解

在这里插入图片描述

LinkedHashSet

  • LinkedHashSet:底层由哈希表和链表组成
  • 哈希表保证元素的唯一性
  • 链表保证元素的顺序,有序,(存储和取出的顺序一致)
public static void main(String[] args) {

        LinkedHashSet<String> strings = new LinkedHashSet<String>();

        strings.add("hello");
        strings.add("world");
        strings.add("java");

        for(String s:strings){
            System.out.println(s);
        }
    }
}
//hello
//world
//java
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-08-13 12:07:06  更:2021-08-13 12:07:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:22:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码