Блок 1. Раздел 5. Тема 4
Сравниваем два массива на равенство. Копируем и вставляем массивы друг в друга
Чтобы сравнить два массива, нужно совершить обход обоих массивов и сравнить их поэлементно.
Рассмотрим к примеру пару наборов чисел:
	1, 10, 5
	1, 10, 70
Равны ли они? Нет, потому что их последние элементы отличаются. То есть нужно сравнить сначала нулевые элементы, затем первые затем вторые и так далее до конца. Получается нам достаточно всего одной переменной, которая пробежит значения 0, 1, 2, 3, … N-1, где N – длина массивов, если конечно она у них совпадает.

Заранее имеет смысл сравнить их размеры, т.к. если они разные, то и массивы считаем разными. Имеет смысл оформить это в виде функции, чтобы затем можно было её где угодно использовать.
static boolean compareArrays (int [] ar1, int [] ar2) {
//сравним длины
	if (ar1.length != ar2.length )
		//return завершает функцию на данном моменте
		//мы вернули false, т.к. массивы не равны:
		return false; 
// если программа дошла до сюда, значит if не сработал, ведь в нем был return
//теперь совершим обход массивов в поисках не равных элементов:
//обычная шапка – идем по обоим массивам вместе:
	for (int i = 0; i < ar1.length; i ++)
		if (ar1 [i] != ar2 [i]) //если нашли два неравных, то вернуть false
			return false;
/*раз ни один из if внутри цикла не сработал и программа до сюда дошла, то 
ВСЕ элементы на iых местах в ar1 и ar2 были равны. Смело возвращаем true:*/
	return true;
}
//Теперь в main мы можем протестировать эту функцию:
public static void main () {
	int [] ar1 = {1, 4, 6};
	int [] ar2 = {1, 4, 6};
	int [] ar3 = {1, 9, 9};
	System.out.println (compareArrays (ar1, ar2));//true-равны
	System.out.println (compareArrays (ar2, ar3));//false-неравны
}
В этой функции есть главный аспект и главное место, где легко ошибиться: часто хочется прямо рядом с if внутри цикла написать else return true; был бы такой код
	for (int i = 0; i < ar1.length; i ++)
		if (ar1 [i] != ar2 [i]) //если нашли два неравных, то вернуть false
			return false;
		else return true;  /* ОШИБКА – мы сравним не весь массив, а лишь начальные элементы */
Подумайте над этим – почему так получилось?..

Дело в том, что только лишь такой цикл начнет свою работу – так сразу же её и закончит – если ar1 [0] не равен ar2 [0], то вернет false (и правильно, ведь если в одном месте неравенство, значит и массивы считаем неравными), а если равен, то вернет true. Но ведь чтобы сравнить полностью массивы, необходимо просмотреть все их элементы, а не только нулевые. Поэтому следует прогнать цикл полностью до самого хвоста и только тогда уже возвращать true, если if внутри цикла так ни разу и не сработал.
Для этой цели есть ещё второе решение той же задачи, посмотрите внимательно:
static boolean compareArrays (int [] ar1, int [] ar2) {
//сравним длины
	if (ar1.length != ar2.length )
		return false; 
	boolean result = true;
	for (int i = 0; i < ar1.length; i ++)
		if (ar1 [i] != ar2 [i]) {
			result = false;
			break; //break останавливает цикл
		}
	return result; 
}
Здесь, если хоть один раз выполнился if, то есть нашлась пара неравных между собой элементов, то переменная result получает значение false. Если же ни одного раза if не выполнился, то result оставляет свое прошлое значение true. Этот подход будет нужен вам и в дальнейшем – проверить, есть ли хоть один элемент не удовлетворяющий условию, или же иначе все абсолютно элементы ему удовлетворяют. Возьмите эту систему с объявлением дополнительной переменной result себе на вооружение – во многих алгоритмах бывает нужен этот фрагмент.
Копируем и вставляем массивы друг в друга
Пусть есть два массива и есть задача склеить их в один третий подряд друг за другом. Это бывает нужно сплошь и рядом, ведь все мы привыкли к операции copy+paste – и это один из её вариантов. По-научному склейка – конкатенация, так что можно назвать эту функцию concat:
static int[] concatArrays (int [] ar1, int [] ar2) {
//выделим память под новый большой массив-склейку.
	int [] ar3 = new int [ar1.length + ar2.length];
//скопируем в начало первый массив в начало второго:
	for (int i = 0; i < ar1.length; i ++)
		ar3 [i] = ar1 [i];
//скопируем второй массив сразу после первого:
	for (int i = 0; i < ar2.length; i ++)
		ar3 [ar1.length + i] = ar2 [i]; //следите за тем, чтобы дать нужный номер элемента в квадратных скобках. ar3 приравнивается к ar2
	
	return ar3;
}
//в main сможем использовать так:
public static void main () {
	int [] some1 = {1, 2, 3};
	int [] some2 = {4, 5, 6};
// вызовем функцию и возвращаемый массив запишем в some3
	int [] some3 = concatArrays (some1, some2);
	printArray (some3); // будет 1 2 3 4 5 6 – два массива склеены в новом
}
Поиск заданного элемента, минимального элемента и подсчет встречаемости элементов
Заданный элемент ищется так:
static int[] concatArrays (int [] ar1, int [] ar2) {
//выделим память под новый большой массив-склейку.
	int [] ar3 = new int [ar1.length + ar2.length];
//скопируем в начало первый массив в начало второго:
	for (int i = 0; i < ar1.length; i ++)
		ar3 [i] = ar1 [i];
//скопируем второй массив сразу после первого:
	for (int i = 0; i < ar2.length; i ++)
		ar3 [ar1.length + i] = ar2 [i]; //следите за тем, чтобы дать нужный номер элемента в квадратных скобках. ar3 приравнивается к ar2
	
	return ar3;
}
//в main сможем использовать так:
public static void main () {
	int [] some1 = {1, 2, 3};
	int [] some2 = {4, 5, 6};
// вызовем функцию и возвращаемый массив запишем в some3
	int [] some3 = concatArrays (some1, some2);
	printArray (some3); // будет 1 2 3 4 5 6 – два массива склеены в новом
}
Поиск минимального элемента пишется так:
static int searchMin (int [] ar) {
	int curMin = ar [0];//быть может нулевой – минимальный?
	for (int i = 0; i < ar.length; i ++)
		if (ar[i] < curMin) //если есть меньше минимального
			curMin = ar [i]; // то мы возьмем новый минимальный
//если мы бы хотели узнать, ГДЕ был минимум, то сохранили бы i
	return curMin; 
}
Подсчет встречаемости заданного символа в строке пишется так:
static int countSym (char [] str, char sym) {
	int count = 0;
	for (int i = 0; i < str.length; i ++)
		if (str [i] == sym)
			count ++;//если нашли символ, то увеличили count

	return count;
}
Такой подсчет называется сбором статистики о встречаемости элемента.
Меняем два элемента местами. Разворачиваем массив задом наперед.
Пусть есть задача поменять два элемента в массиве местами. Это очень часто бывает нужно в более сложных повсеместно используемых алгоритмах – например разворот массиве задом наперед, сортировка массива, и других. Поменять два элемента местами можно к примеру так. Пусть их индексы – это i, j.
	int tmp = ar [i]; //сохранили i-ый элемент в tmp
	ar [i] = ar [j]; //мы забыли элемент ar[i], записав в него j-ый
	ar[j] = tmp; //записали сохраненное в tmp значение в j-ую ячейку
//подумайте над этим простым алгоритмом. Его нужно хорошо понимать.
Теперь мы можем реализовать более сложный алгоритм – разворот массива задом наперед. Он часто бывает нужен при анализе любых данных, когда они изначально записаны в неудобном порядке в силу каких-то внешних обстоятельств и таким поступили вам на вход. Оформим решение в виде функции:
static void inverseArray (int [] ar) {
	int tmp = 0;

	for (int i = 0; i < ar.length/2; i ++) {
//меняем местами элементы под номерам:
//с одной стороны “i”, с другой “ar.length-1-i”
//то есть меняем элементы местами с начала и конца
		tmp = ar [i];
		ar [i] = ar [ar.length – 1 – i];
		ar [ar.length-1-i] = tmp;
	}
}
//заметьте, что условие i < ar.length/2 дает нам развернуть массив один раз
// если мы оставим старое i < ar.length, то развернем массив дважды
// и он в итоге останется точно тем же, что и был в начале
//Для запуска этой функции в main сделаем такой код:
public static void main () {
	int [] ar1 = {9, 8, 7, 6, 5, 4, 3, 2, 1};
	inverseArray (ar1);
	printArray (ar1);
}
Замечание о том, что массив – ссылочный тип, и что с ним сделается в функции, то сделается с ним и во вне.

Мы с вами написали inverseArray и она принимает на вход массив. Мы в ней меняем элементы местами, и в итоге разворачиваем весь массив задом наперед. Мы вызвали inverseArray в main и массив действительно поменял свои значения – распечатка printArray в main показала, что он правда изменился. Но не так ведут себя обычные «примитивные» типы int, double, char, рассмотрим пример:
void increase (int x) 
{
	 x = x +1; //казалось бы икс увеличился на 1
}
public static void main () {
	int y = 0;
	increase (y);
	System.out.println (y);
//как игрек был нулем, так нулем и остался – печально..
}
Дело в том, что когда мы передаем примитивные типы параметром, то создается копия их значения, и с ней идет вся работа внутри функций. Под эти копии выделяется своя память и именно с этой новой памятью совершаются все действие – на значение, переданное при запуске, к примеру, в main, это никак не влияет вообще. Другое дело массивы – это ссылочный тип. Если его передать, то новая память не создается – и действительно, массивы ведь часто занимают огромное количество памяти, было бы долго и неэффективно без конца их копировать. Поэтому в Java принято, что если программист хочет явно сделать новый массив, то пусть пишет слово new. А иначе речь все время об одном и том же массиве. Так что и когда он в функцию передается, то это тот самый, а не какой-то скопированный массив, и мы его меняя в функции, изменяем и в main и в любом месте, где наша функция вызовется.

Это касается и обычных присваиваний. Сделаем «два» массива присваиванием:
int [] ar1 = {1, 2, 3};
int [] ar2 = ar1; //присвоена только ссылка, так что реально это тот же массив
ar2 [0] = 10; //изменились оба массива! Не только ar2.
Systtem.out.println (ar1[0]); //распечатается 10, хоть мы и изменили ar2
	С переменными примитивных типов другая история – при присваивании копируется значение. Это не ссылки, а просто значения. К этому мы уже и привыкли:
int x = 1;
int y = x;
y = 10;
System.out.println (x); //по-прежнему 10, ведь y с иксом – разные переменные
Самое главное мы уже освоили. Теперь время решать задачи. Ответьте на несколько вопросов, чтобы лучше усвоился новый материал:
Вопрос 1. Что значит объявить переменную? (ответы по кнопке "плюс")
Это значит назвать число заданного типа (целые, дробные или коды букв) каким-то своим именем. Под это число будет выделено место в оперативной памяти. Это число в дальнейшем можно менять и использовать в любых расчетах.
Вопрос 2. Что будет, если объявить две переменных с одним и тем же именем внутри одного блока фигурных скобок { } ?
Компилятор выдаст ошибку, потому что с его точки зрения, вы говорите, что одно и то же имя теперь может отвечать двум разным переменным. Он не может разобраться, какой же из двух переменных это имя отвечает, и сообщит, что это имя двусмысленное и его нужно изменить.

То есть в таком коде есть ошибка:
int x = 100;
int x = 200; // ошибка, x уже был объявлен
А в таком коде все нормально:
int x = 100;
x = 200; //поменять значение x на 200
//именно отсутствие int в начале строчки говорит о том, что x просто меняет свое значение, и мы не пытаемся завести новую переменную.
Задача 1. Ниже дан код с двумя переменными. Что будет распечатано на экране?
package javaapplication1;
public class JavaApplication1 {
public static void main(String[] args) {
  int x = 100;
  x = x*10/2 + x;
  System.out.println (x);

  double y = x - 5.5;

  System.out.println (y);
 }   
}
Ответ
600
594.5
Решение
Посчитаем выражения для введенных в коде переменных x и y.
Сначала x = 100;
Затем в строчке
x = x*10/2 + x;
компилятор сначала рассчитывает числовое выражение справа от равно
на основе старого значения x
100*10/2 + 100, то есть 600.
Поэтому строчка читается как "икс присвоить 600". Это значение и будет распечатано на экране.

На его основе будет вычислено значение y = x - 5.5, то есть 594.5.
Переменная y обладает типом double, то есть дробное, поэтому в ней можно сохранить дробную часть 0.5 . Если бы y было целым, то в такой переменной невозможно было бы сохранить дробную часть, и компилятор подчеркнул бы эту строчку красным и выдал ошибку. В ошибке было бы сказано, что идет отбрасывание дробной части, и это может привести к потере точности.
Решайте больше задач по этому разделу здесь.
В следующей теме расскажем подробнее
о типах данных в Java