Почему поиск максимального значения в массиве происходит неверно: вместо 10 выводится 7?

Задача следующая:

Пользователь вводит значение в массив и исходя из его значений выводится максимальное значение:

Мой код:

public class max_number{
    public static void main(String[] args) {
        int[] a;
        int n;
        int max3 = arr[0];
        Scanner in = new Scanner(System.in);
        System.out.println("Сколько будет значений?");
        n = in.nextInt();
        a = new int[n];
        for (int i = 0; i <n; i++){
            System.out.println("Выведи значение для" + i + ": ");
            a[i] = in.nextInt();
            if (arr[i] > max3){
               max3 = arr[i];
            }
        }
        System.out.println(max3);
    }
}

Фактический результат сейчас:
Пользователь указывает, что в массиве будет 3 значения: 5, 7, 10.
Максимальное значение сейчас выводит 7, а ожидаемое - 10:

Вывод в терминале


Ответы (3 шт):

Автор решения: Швеев Алексей

Ну начнём с того, что у вас переменная называется то arr, то a... Буд то не вы вовсе код писали) Да и запустить его не получится

Заметим, что вы выполняете

int max3 = arr[0];

ещё до того, как arr был проинициализирован. Перенесём эту строку ниже

Вот что получается:

public class max_number {
    public static void main(String[] args) {
        int[] arr;
        int n;
        Scanner in = new Scanner(System.in);
        System.out.println("Сколько будет значений?");
        n = in.nextInt();
        arr = new int[n];
        int max3 = arr[0];
        for (int i = 0; i < n; i++){
            System.out.println("Выведи значение для" + i + ": ");
            arr[i] = in.nextInt();
            if (arr[i] > max3){
               max3 = arr[i];
            }
        }
        System.out.println(max3);
    }
}

После небольшого изучения кода заметим, что массив используется только для хранения текущего значения, а значит мы его можно просто поменять на обычную переменную

Предполагаю что изначально был запланирован следующий алгоритм:

  1. Создаём массив длины n
  2. Проходимся по каждому элементу массива и вписывает в него введённое пользователем значение
  3. Проходимся по каждому элемент массива и ищем наибольшее
  4. Выводим его

В данном контексте массив будет полезнее, так как в будущем мы сможем передавать любой массив.

Однако в данном конкретном случае он вообще никак не используется, и по сути алгоритм такой:

  1. Проходимся в массиве от 0 до n
    1. Запрашиваем число от пользователя
    2. Если число больше текущего максимального, то меняем максимальное на текущее
  2. Выводим результат

Как мы видим массив нам и не нужен, и код легко можно переписать на такой:

public class max_number {
    public static void main(String[] args) {
        int[] arr;
        Scanner in = new Scanner(System.in);
        
        System.out.println("Сколько будет значений?");
        int n = in.nextInt();
        int max = 0;
        
        for (int i = 0; i < n; i++){
            System.out.println("Выведи значение для " + i + ": ");
            int currentValue = in.nextInt();
            if (currentValue > max){
               max = currentValue;
            }
        }
        System.out.println(max);
    }
}

Данный код успешно работает с положительными числами. Однако если мы введём отрицательное, то максимальное будет равно нулю. Есть простых способа решить эту проблему:

  1. Задать min на минимальное допустимое число для типа int
  2. Задавать min первым введённым числом

Мы будем решать через второй вариант. Вынесем первую итерацию цикла из цикла:

System.out.println("Выведи значение для " + 0 + ": ");
int max = in.nextInt();

И следовательно уберём первую итерацию из самого цикла:

for (int i = 1; i < n; i++) {...}

Финальный код:

public class max_number {
    public static void main(String[] args) {
        int[] arr;
        Scanner in = new Scanner(System.in);
        
        System.out.println("Сколько будет значений?");
        int n = in.nextInt();
        
        // Итерация 0:
        System.out.println("Выведи значение для " + 0 + ": ");
        int max = in.nextInt();
        
        // Итерации 1..n:
        for (int i = 1; i < n; i++){
            System.out.println("Выведи значение для " + i + ": ");
            int currentValue = in.nextInt();
            if (currentValue > max){
               max = currentValue;
            }
        }
        System.out.println(max);
    }
}
→ Ссылка
Автор решения: Зонтик

Давайте попробуем составить алгоритм действий и в то же время пройтись по вашему коду, проверяя, все ли пункты правильно выполнены и нет ли ничего лишнего.

  1. Получаем от пользователя количество чисел и инициализируем массив, предварительно проверяя, что длинна массива не меньше 1.

Ваши ошибки на этом шаге:

  • Массива почему-то 2: a и arr. Нужен только один. Пусть это будет array.
  • Переменные лучше называть говорящими и понятными именами. Поэтому in лучше заменить на Scanner, а n например на length.
  • Нужно проверить, что пользователь ввёл корректную длину массива (то есть не 0 и не отрицательное число). Можно сделать так, что программа не пойдёт дальше, пока число не будет правильным.

Тогда код из этого пункта алгоритма будет такой:

Scanner scanner = new Scanner(System.in);
int length = 1;
while(true){
    System.out.println("Сколько будет значений?");
    length = scanner.nextInt();
    if(length < 1) System.err.println("Длинна массива не может быть " + length + "!");
    else break;

}
int[] array = new int[length];
  1. Объявляем переменную и приравниваем её минимальному значению типа int. Здесь у меня вопрос: почему у вас переменная называется max3? Почему не просто max (а лучше бы maxNumber)?

Минимальное значение типа int удобно взять из константы MIN_VALUE класса Integer:

int maxNumber = Integer.MIN_VALUE;
  1. Получаем числа от пользователя. На этом этапе ошибка одна: эти размножающиеся массивы. Массив нужен только один. Реализация этого пункта алгоритма может выглядеть так:
for(int i = 0; i < array.length; i++){
    System.out.println("Введи следующий индекс массива:");
    array[i] = scanner.nextInt();
}
  1. Собственно ищем максимум (в том же самом цикле, где просили вводить пользователя числа). Это реализовано у вас правильно, но можно записать if короче:
if(array[i] > maxNumber) maxNumber = array[i];
  1. Выводим максимум на экран.

P.S: прошу внимательно прочитать ответ и понять все ошибки. Тогда вы без труда склеите приведённые лоскутки кода в единое целое.

→ Ссылка
Автор решения: Nowhere Man

Проблема, указанная в заголовке вопроса: Почему поиск максимального значения в массиве происходит неверно: вместо 10 выводится 7? НЕ воспроизводится из-за ошибок в коде, которые необходимо исправить:

  1. Ошибки при объявлении переменных.
    В "классическом" стиле программирования требовалось сначала объявить переменные, и только затем заниматься действиями с ними, включая инициализацию. В данном случае это привело к путанице с именем массива.
    Чтобы избежать таких ошибок, достаточно объявлять переменные непосредственно при присваивании их значений, чтобы не было лишнего кода между объявлением и присваиванием. Также следует определиться с названием массива.
Scanner in = new Scanner(System.in);
System.out.println("Сколько будет значений?");
int n = in.nextInt();
int[] arr = new int[n];
// ...
  1. Инициализация максимума.
    В исходном коде максимум инициализируется первым членом массива до того как массив был создан. Но даже если непустой массив был создан правильно, он будет содержать нули, и соответственно максимуму будет присвоено значение 0, что приведёт к неправильному решению, если при вводе массив будет заполнен только отрицательными значениями.

В своём ответе Зонтик проинициализировал максимум наименьшим целочисленным значением Integer.MIN_VALUE, что вполне приемлемо для заведомо непустого массива.

Однако следует учесть, что n = 0 является вполне допустимым размером массива, тогда он будет пустой, и максимум как таковой вообще не будет существовать.
То есть, имеет смысл объявить переменную для хранения максимума так:

Integer max = null;

Также для поиска максимума можно использовать стандартный метод Math.max, с проверкой текущего максимума:

for (int i = 0; i < n; i++) {
    System.out.println("Введи значение для " + i + ": ");
    arr[i] = in.nextInt();
            
    max = Math.max(arr[i], max == null ? Integer.MIN_VALUE : max);
}
System.out.println(max);

Такое решение корректно обработает ситуации для корректной длины массива (неотрицательного числа), и найдёт максимум для любых элементов массива.

В случае неправильного ввода / отрицательной величины массива будут выброшены соответствующие непроверяемые исключения.


Однако даже исправленное решение представляет собой "монолит", где один метод отвечает за ввод данных, их обработку (поиск максимума) и вывод результата.

Поэтому имеет смысл улучшить приведённое решение, разбив исходную задачу на подзадачи при помощи отдельных методов, чтобы каждый из них отвечал за своё действие:

  1. создаёт входной массив целых чисел (при помощи сканера потока ввода)
  2. ищет максимум в массиве
  3. объединяет вызовы двух вышеупомянутых методов и выводит результаты.

Исправленное решение можно представить в виде отдельного класса FindMax и класса-драйвера Main:

public class Main {
    public static void main(String ... args) {
        new FindMax().execute();
    }
}

Код станет более структурированным:

public class FindMax {
    public void execute() {
        int[] arr = inputArray();
        System.out.println("Введён массив: " + Arrays.toString(arr));
        Integer max = findMax(arr);
        System.out.println(max);
    }
    
    protected int[] inputArray() {
        Scanner in = new Scanner(System.in);
        System.out.println("Введите размер массива: ");
        
        int[] arr = new int[in.nextInt()];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = in.nextInt();
        }
        
        return arr;
    }
    
    protected Integer findMax(int ... arr) {
        Integer max = null;
        for (int x : arr) {
            max = null == max ? x : Math.max(x, max.intValue());
        }
        return max;
    }
}

Такая организация кода (см. шаблонный метод) позволит в дальнейшем переопределить реализации методов как ввода, так и поиска максимума, например с помощью Stream API код можно значительно сократить:

class FindMaxStream extends FindMax {
    protected int[] inputArray() {
        Scanner in = new Scanner(System.in);
        System.out.println("Введите размер массива: ");
        
        return IntStream.generate(in::nextInt).limit(in.nextInt()).toArray();
    }
    
    protected Integer findMax(int ... arr) {
        OptionalInt max = Arrays.stream(arr).max(); 
        return max.isPresent() ? max.getAsInt() : null;
    }
}
→ Ссылка