Java初识泛型 | 如何通过泛型类/泛型方法实现求数组元素最大值?

简介: 这是一个关于如何使用泛型在Java中找到数组中最大值的问题。

一、引言


给定一个整型数组,求数组中所有元素的最大值


该题同样是编程入门的基础题。关键在于拓展:能不能通过程序,实现给定的任意类型的数组,都能求出数组中所有元素的最大值?

这里我们先给出整型数组求最大值的代码,作为基础:


public class Util {
    public int findMaxInt(int[] array) {
        int max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(max < array[i]) {
                max = array[i];
            }
        }
        return max;
    }
}


调用并运行:

public class Test {
    public static void main(String[] args) {
        int[] array = {1,0,9,4,5};
        System.out.println("max is " + util.findMaxInt(array));
    }
}



接下来我们来分析,如果通过泛型类或泛型方法使程序能够适用于任意一个数据类型。


二、编程分析

1、泛型类实现


解题之前,我们首先要了解泛型、泛型上界、泛型方法的概念。对于这些概念有所遗忘的同学,可以阅读文章:🔗Java初识泛型 | 如何通过泛型类/泛型方法获取任意类型的三个数的最大值?的知识解析部分熟悉一下。


这里,我们直接讲解思路,不再进行基础知识的铺垫。


思路


按照泛型的思路,我们试着将原来的类改成泛型类。用占位符E代替原代码中所有的int。但仅仅更改这些,最后会发现编译器在比较的位置报错了:



是什么原因导致编译失败呢?



原因:max和array[i]都是E类型也就是引用类型的变量,两个引用变量之间不能用大于小于号进行大小比较。可行的方法应当是调用比较对象的compareTo()方法。但是,compareTo()方法是不能直接调用的,它要求调用的对象的类型必须实现过Comparable接口。



如图 尝试直接调用compareTo()方法失败


换句话说,它对类型E有约束,这个约束就是: 必须实现过Comparable接口。因此,这里要用到类型的上界的知识,来进行约束。


public class Util<E extends Comparable<E>> {
    ...
}


<E extends Comparable<E>> 的含义为:传入的E类型必须是实现了Comparable接口的。如果传入的类型没有实现过Comparable接口,那么编译会报错。

此时,我们可以成功地调用compareTo()方法了:



此时调用成功


整合后,便能得出完整的代码了。


代码


泛型类:


public class Util<E extends Comparable<E>> {
    public E findMax(E[] array) {
        E max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}
 


调用:


Integer


1.public class Test {
    public static void main(String[] args) {
        Util<Integer> util = new Util<>();
        Integer[] array = {1,0,9,4,5};
        System.out.println("max is " + util.findMax(array));
    }
}



Double(只需更改main中的数据类型即可,Util类不用变动)


public class Test {
    public static void main(String[] args) {
        Util<Double> util = new Util<>();
        Double[] array = {13.6,100.5,0.0,-3.1,5.8};
        System.out.println("max is " + util.findMax(array));
    }
}



String 


public class Test {
    public static void main(String[] args) {
        Util<String> util = new Util<>();
        String[] array = {"China","America","Australia","Ireland"};
        System.out.println("max is " + util.findMax(array));
    }
}



自定义的类型Tmp,已手动实现了Comparable<Tmp>接口


//Tmp.java
 
public class Tmp implements Comparable<Tmp>{
    public int parameter1;
    public String parameter2;
 
    public Tmp(int parameter1, String parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
    }
 
    @Override
    public int compareTo(Tmp o) {
        //以parameter2为标准来比较Tmp
        //parameter2是String类,比较可以直接调用String类的compareTo方法
        return this.parameter2.compareTo(o.parameter2);
    }
 
    @Override
    public String toString() {
        return "Tmp{" +
                "parameter1=" + parameter1 +
                ", parameter2='" + parameter2 + '\'' +
                '}';
    }
}



//Test.java
 
public class Test {
    public static void main(String[] args) {
        Util<Tmp> util = new Util<>();
        Tmp tmp1 = new Tmp(10,"substantial");
        Tmp tmp2 = new Tmp(20,"circumspect");
        Tmp tmp3 = new Tmp(5,"coby");
        Tmp[] array = {tmp1,tmp2,tmp3};
        System.out.println("max is " + util.findMax(array));
    }
}



2、泛型方法实现


思路


如果用泛型类实现,则每次使用前还需要创建类对象。能不能把这一个步骤省去呢?答案是可以的,我们可以通过静态泛型方法来完成。


按照泛型方法的语法,我们对原代码稍作修改:


public class Util {
    public static  <E>  E findMax(E[] array) {
        ...
    }
}


此时的Util类已经不再是泛型类了,而是一个普通类。通过在修饰符后添加 static <E> 将普通方法改为了静态泛型方法。但此时还不够完善,同样的,我们需要把类型参数E实现了Comparable接口这一约束体现出来。只需要把<E>改为 <E extends Comparable<E>> 即可。


代码

public class Util {
    public static  <E extends Comparable<E>>  E findMax(E[] array) {
        E max = array[0];
        for (int i = 0; i < array.length; i++) {
            if(max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}


此时,测试时的代码也更加简略了:


public class Test {
    public static void main(String[] args) {
        Tmp tmp1 = new Tmp(10,"substantial");
        Tmp tmp2 = new Tmp(20,"circumspect");
        Tmp tmp3 = new Tmp(5,"coby");
        Tmp[] array = {tmp1,tmp2,tmp3};
        System.out.println("max is " + Util.findMax(array));
    }
}


依旧能够正确运行


三、拓展:数组排序(以冒泡排序为例)


1、int类型 原代码


//Tmp.java
 
public class Tmp{
    public static void bubbleSort(int[] array){
        for (int i = 0; i < array.length-1; i++) {
            boolean flag = false;
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j] > array[j+1]) {
                    int tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    flag = true;
                }
            }
            if(!flag) {
                break;
            }
        }
    }
}


//Test.java
 
import java.util.Arrays;
 
public class Test {
    public static void main(String[] args) {
        int[] array = {0,3,1,8,9,6,4,5,2};
        Tmp.bubbleSort(array);
        System.out.println(Arrays.toString(array));
    }
}



2、泛型类


//Util.java
 
public class Util<E extends Comparable<E>> {
    public void bubbleSort(E[] array){
        for (int i = 0; i < array.length-1; i++) {
            boolean flag = false;
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j].compareTo(array[j+1]) > 0) {
                    E tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    flag = true;
                }
            }
            if(!flag) {
                break;
            }
        }
    }
}


//Test.java
 
import java.util.Arrays;
 
public class Test {
    public static void main(String[] args) {
        Util<Integer> util = new Util<>();
        Integer[] array = {10,8,-33,7,21,78};
        util.bubbleSort(array);
        System.out.println(Arrays.toString(array));
    }
}



3、泛型方法


//Util.java
 
public class Util {
    public static <E extends Comparable<E>> void bubbleSort(E[] array){
        for (int i = 0; i < array.length-1; i++) {
            boolean flag = false;
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j].compareTo(array[j+1]) > 0) {
                    E tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                    flag = true;
                }
            }
            if(!flag) {
                break;
            }
        }
    }
}
 


//Test.java
 
import java.util.Arrays;
 
public class Test {
    public static void main(String[] args) {
        Integer[] array = {10,8,-33,7,21,78};
        Util.bubbleSort(array);
        System.out.println(Arrays.toString(array));
    }
}



拓展阅读: 🔗Java初识泛型 | 如何通过泛型类/泛型方法获取任意类型的三个数的最大值?

相关文章
|
1月前
|
存储 缓存 安全
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
30 2
|
2月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
48 3
|
2月前
|
Java
在Java的世界里,Set只接纳独一无二的元素。
【10月更文挑战第16天】在Java的世界里,Set只接纳独一无二的元素。本文通过拟人化的手法,讲述了重复元素从初次尝试加入Set被拒绝,到经历挣扎、反思,最终通过改变自己,成为独特个体并被Set接纳的全过程。示例代码展示了这一过程的技术实现。
26 1
|
1月前
|
Java
那些与Java Set擦肩而过的重复元素,都经历了什么?
在Java的世界里,Set如同一位浪漫而坚定的恋人,只对独一无二的元素情有独钟。重复元素虽屡遭拒绝,但通过反思和成长,最终变得独特,赢得了Set的认可。示例代码展示了这一过程,揭示了成长与独特性的浪漫故事。
22 4
|
1月前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
1月前
|
存储 算法 Java
为什么Java Set如此“挑剔”,连重复元素都容不下?
在Java的集合框架中,Set是一个独特的接口,它严格要求元素不重复,适用于需要唯一性约束的场景。Set通过内部数据结构(如哈希表或红黑树)和算法(如哈希值和equals()方法)实现这一特性,自动过滤重复元素,简化处理逻辑。示例代码展示了Set如何自动忽略重复元素。
31 1
|
2月前
|
存储 缓存 算法
Java 数组
【10月更文挑战第19天】Java 数组是一种非常实用的数据结构,它为我们提供了一种简单而有效的方式来存储和管理数据。通过合理地使用数组,我们能够提高程序的运行效率和代码的可读性。更加深入地了解和掌握 Java 数组的特性和应用,为我们的编程之旅增添更多的精彩。
36 4
|
2月前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
53 0
[Java]泛型
|
2月前
|
存储 缓存 算法
提高 Java 数组性能的方法
【10月更文挑战第19天】深入探讨了提高 Java 数组性能的多种方法。通过合理运用这些策略,我们可以在处理数组时获得更好的性能表现,提升程序的运行效率。
43 2
|
2月前
|
Java 开发者
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素
在Java集合世界中,Set以其独特的特性脱颖而出,专门应对重复元素。通过哈希表和红黑树两种模式,Set能够高效地识别并拒绝重复元素的入侵,确保集合的纯净。无论是HashSet还是TreeSet,都能在不同的场景下发挥出色的表现,成为开发者手中的利器。
30 2