Category Archive: Java

Экранирование символов в названии файла

Всем нам известно, что нельзя использовать некоторые символы в названиях файлов. И на разных операционных системах эти запрещенные символы различны. Например, в Windows это следующие символы: \ / : * ? < > | invalid_characters Если в вашем приложении названия файлов генерируются автоматически, то нужно подстраховаться, заменив запрещенные символы на какой-нибудь разрешенный. Ниже будет представлена функция, которая заменяет перечисленные выше символы на символ подчеркивания "_":
public static String screeningFileName(String fileName){
	return fileName.replaceAll("[\\\\/:*?\"<>|]","_");
}
Пример использования:
System.out.println(screeningFileName("report*2014-04-29 17:46:00.pdf"));
//результат выполнения функции: report_2014-04-29 17_46_00.pdf 

Отправка письма через SMTP c NTLM-аутентификацией

Однажды, придя к заказчику, я не смог подключиться к их заранее заведенному электронному адресу для рассылок. Назовем его noreply@sample.com. Пришлось демонстрировать систему с ящиком yandex.ru. Основное предположение было то, что дело в домене. Так и оказалось, для подключения к SMTP нужно было пройти NTLM-аутентификацию (Статья о NTLM в Википедии). Сперва у меня не получалось, т.к. использовал старую библиотеку mail.jar (версия: 1.4, размер: 379 КБ), потом скачал новую версию библиотеки, которая поддерживает NTLM: javax.mail.jar (версия: 1.5, размер: 533 КБ). Эту версию можно скачать отсюда: javax.mail.jar В этот статье вы встретите две реализации: одна реализация с обычным подключением к SMTP, а другая реализация с NTLM-авторизацией. Для начала рассмотрим обычное подключение, чтобы потом увидеть разницу. Созданим класс MailHelper, в конструкторе этого класса будут задаваться все необходимые параметры для подключения к почтовому ящику, а метод send будет отвечать за отправку писем согласно переданным в этот метод параметрам:
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;

public class MailHelper {
		
	private Properties properties;
	private Session session;
	private String currentEmail;
	
	public MailHelper(String host,String port,final String user,final String password){
		properties = new Properties();
		properties.setProperty("mail.smtp.auth","true");
		properties.setProperty("mail.smtp.host", host);
		properties.setProperty("mail.smtp.port",port);
		currentEmail = user;
		session = Session.getInstance(properties,
		  new javax.mail.Authenticator() {
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication(user,password);
      			}
      		  });
	}
	
	public void send(String email,String subject,String body) throws Exception{
		MimeMessage msg = new MimeMessage(session);
		msg.setFrom(new InternetAddress(currentEmail));
		msg.setRecipient(Message.RecipientType.TO, new InternetAddress(email));
		msg.setSubject(subject);
		msg.setContent(body, "text/html; charset=utf-8");
		Transport.send(msg);
	}
}
Пример использования этого класса:
MailHelper mailHelper = new MailHelper(
		"smtp.sample.com","25",
		"noreply@sample.com","password123");
try {
	mailHelper.send("target@sample.com", "Проверка", "Привет, Target!");
} catch (Exception e) {
	e.printStackTrace();
}
В конструктор передаются: адрес SMTP-сервера, порт, логин и пароль. В метод send передаются: электронный адрес получателя, заголовок и содержимое письма. Теперь рассмотрим реализацию с NTLM. Практически тоже самое, только при создании объекта класса PasswordAuthentication нужно указать имя учетной записи в домене, а адрес электронной почты передается отдельным параметром.
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;

public class MailHelper {
		
	private Properties properties;
	private Session session;
	private String currentEmail;
	
	public MailHelper(String host,String port,
		final String user,final String password,String email){
		properties = new Properties();
		properties.setProperty("mail.smtp.auth","true");
		properties.setProperty("mail.smtp.host", host);
		properties.setProperty("mail.smtp.port",port);
		currentEmail = email;
		session = Session.getInstance(properties,
      		  new javax.mail.Authenticator() {
      			protected PasswordAuthentication getPasswordAuthentication() {
      				return new PasswordAuthentication(user,password);
      			}
      		  });
	}
	
	public void send(String email,String subject,String body) throws Exception{
		MimeMessage msg = new MimeMessage(session);
		msg.setFrom(new InternetAddress(currentEmail));
		msg.setRecipient(Message.RecipientType.TO, new InternetAddress(email));
		msg.setSubject(subject);
		msg.setContent(body, "text/html; charset=utf-8");
		Transport.send(msg);
	}
}

Пример использования:
MailHelper mailHelper = new MailHelper("192.168.0.2","25",
	"noreply","password123","noreply@sample.com");
try {
	mailHelper.send("target@sample.com", "Проверка", "Привет, Target!");
} catch (Exception e) {
	e.printStackTrace();
}
Конструктор принимает следующий параметры: Адрес и порт SMTP-сервера, доменную учетную запись и пароль, а также адрес электронной почты, от имени которой будут уходить письма. Метод send принимает те же параметры, что и в предыдущем примере: адрес получателя, тема и содержимое письма. В итоге на указанный ящик должно прийти сообщение:
Привет, Мир! 

НОК и НОД (lcm и gcd) на Java

НОД (Наибольший общий делитель) или gcd (Greatest Common Divisor) НОД - наибольшее число, которое является делителем одновременно для чисел a и b. Реализация (Алгоритм Евклида):
long gcd(long a,long b){
	return b == 0 ? a : gcd(b,a % b);		
}
Применение:
System.out.println(gcd(10,24));//результат: 2
System.out.println(gcd(12,24));//результат: 12
System.out.println(gcd(11,24));//результат: 1
НОК (Наименьшее общее кратное) или lcm (Least Common Multiple) НОК - наименьшее число, которое делится на a и b без остатка. НОК можно найти через НОД по следующей формуле: нок Реализация:
long lcm(long a,long b){
	return a / gcd(a,b) * b;
}
Примечание: a / gcd(a,b) * b более предпочтительно, чем a * b / gcd(a,b), т.к. во втором случае при больших числах переполнение случиться намного раньше. Применение:
System.out.println(lcm(3, 4));//результат: 12
System.out.println(lcm(3, 9));//результат: 9
System.out.println(lcm(5,12));//результат: 60

Перевод десятичных грудусов в градусы, минуты и секунды

При работе с картами или GPS мы можем получить долготу (longitude) и широту (latitude) в виде виде десятичных грудусов. Например, 37.422006 (широта) и -122.084095 (долгота). Это где-то в Силиконовой долине. Нам может потребоваться перевести эти десятичные градусы в градусы, минуты и секунды, т.е. в формат более понятный человеку. Для справки:
  • Положительное значение широты - северная широта
  • Отрицительное значание широты - южная широта
  • Положительное значение долготы - восточная долгота
  • Отрицательное значение долготы - западная долгота
Преобразование будет реализовано в виде функции, принимающей в качестве параметра десятичный градуc (double), а в качестве результата возвращающая строку вида: X° Y′ Z″, где X - градусы, Y - минуты и Z - секунды. Реализация на Java:
public static String doubleToDegree(double value){
	int degree = (int) value;
	double rawMinute = Math.abs((value % 1) * 60);		
	int minute = (int) rawMinute;
	int second = (int) Math.round((rawMinute % 1) * 60);
	return String.format("%d° %d′ %d″", degree,minute,second);
}
Пример использования:
System.out.println(doubleToDegree(37.422006));//Результат: 37° 25′ 19″
System.out.println(doubleToDegree(-122.084095));//Результат: -122° 5′ 3″
Результаты можно интерпретировать следующим образом: 37° 25′ 19″ означает 37 градусов 25 минут 19 секунд северной широты. -122° 5′ 3″ означает 122 градуса 5 минут 3 секунды западной долготы.

Треугольник Паскаля на Java

Треугольник Паскаля имеет практическое применение в комбинаторике для нахождения сочетания из n по k. Определение из Википедии: В комбинаторике сочетанием из n по k называется набор k элементов, выбранных из данного множества, содержащего n различных элементов. Наборы, отличающиеся только порядком следования элементов (но не составом), считаются одинаковыми, этим сочетания отличаются от размещений. Вы можете встретить два вида обозначения сочетения из n по k:
nk или cnk
Давайте придумаем задачу. Задача Пусть у нас есть множество из 5 элементов: {1,2,3,4,5}. Нужно найти кол-во сочетаний из трех элементов. Причем наборы вида: {1,2,3} и {1,3,2} считаются одинаковыми и засчитываются только один раз. Ответ Из 5 элементов мы можем составить 10 наборов по 3 элемента. Вот эти подмножества:
  1. {1,2,3}
  2. {1,2,4}
  3. {1,2,5}
  4. {1,3,4}
  5. {1,3,5}
  6. {1,4,5}
  7. {2,3,4}
  8. {2,3,5}
  9. {2,4,5}
  10. {3,4,5}
Решение Задача сводится к нахождению сочетания из 5 по 3, т.е. n = 5, k = 3. Для нахождения сочетания есть готовая формула: formula_short Подставим наши значения в формулу: formula c53 Пример реализации этой формулы на Java:
long C(int n,int k){
	return fact(n) / (fact(k) * fact(n-k));
}
long fact(int num){
	long val = 1;
	for (int i = 2; i <= num; i++) {
		val *= i;
	}
	return val;
}
Пример использования:
System.out.println(C(5,3));//возвратит 10
Вычисляя факториалы в формуле, мы проводим вычисления в числителе и знаменателе, и можно заметить, что некоторые операция умножения можно сократить. Усовершенствованная версия:
long C(int n,int k){
	int res = 1;
	for (int i = n - k + 1; i <= n; i++) {
		res *= i;
	}
	
	for (int i = 2; i <= k; i++) {
		res /= i;
	}
	return res;
}
Основная сложность при расчетах - это вычисление факториала. Вычисление факториала числа - очень затратная операция, особенно, если у вас задача с расчетом большого кол-во сочетаний из n по k. Можно, конечно, кэшировать факториалы, но в формуле производятся и другие математические операции. Кроме приведенной выше формулы с факториалами существует другой способ нахождения сочетания из n по k. Способ заключается в построении треугольника Паскаля, из которого уже берутся готовые значения. Классический вид треугольника Паскаля: classic_pascal Как строится треугольник? Треугольник строится построчно сверху вниз. На вершине всегда единица. Каждый элемент в треугольнике получается сложением двух элементом, между которыми и под которыми лежит данный элемент. Причем боковые элементы получаются равными 1. В программе мы будем хранить треугольник Паскаля в двумерном массиве, поэтому он несколько видоизменится: two_dimensional Элементы треугольника - это и есть наши искомые кол-ва сочетаний из n по k: values Теперь приступим к реализации Треугольник будем хранить в двумерном массиве C[][]. Нужно определиться с типом массива, так как числа будут получаться большими, будет переполнение переменных, а вы не заметите. Приведу таблицу типов и предельных значений, после которых будет переполнение в элементах массива.
Тип массиваБезопасный предел
intпри n <= 33
longпри n <= 66
BigIntegerограничено оперативной памятью
(ниже будет рассмотрено более подробно)
Реализация для int и long одинаковая, поэтому рассмотрим только long , а вот для BigInteger немного отличается. Реализация с использованием long-массива:
public class PascalTriangle {
	private int ubound;
	private long C[][];

	//инициализация треугольника
	public PascalTriangle(int ubound){
		if(ubound > 66){
			throw new RuntimeException("Data overflow");
		}
		this.ubound = ubound;
		C = new long[ubound+1][ubound+1];
		for (int n = 0; n <= ubound; n++) {
			//первый и последний элемент в строке равен 1
			C[n][0] = C[n][n] = 1;
			//далее вычисляем внутреннюю часть
			for (int k = 1; k < n; k++) {
				C[n][k] = C[n-1][k-1] + C[n-1][k];  
			}
		}
	}

	//вывод треугольника в консоль
	public void show(){
		for (int n = 0; n <= ubound; n++) {
			for (int k = 0; k <= n; k++) {
				System.out.print(C[n][k] + " ");
			}
			System.out.println();
		}
	}

	//метод, возвращающий кол-во сочетаний из n по k
	public long get(int n,int k){
		return C[n][k];
	}
}
Пример использования:
PascalTriangle pas = new PascalTriangle(66);
//pas.show();
System.out.println(pas.get(5, 3));//результат 10
Теперь рассмотрим случай с BigInteger. BigInteger - это прекрасный класс Java по работе с длинными числами. Все числа хранятся внутри класса, а математические операции производятся с помощью методов класса. Например, операция сложения, которую мы применим в коде, производится с помощью метода add.
public class BigPascalTriangle {
	private int ubound;
	private BigInteger C[][];

	//инициализация треугольника
	public BigPascalTriangle(int ubound){
		this.ubound = ubound;
		C = new BigInteger[ubound+1][ubound+1];
		for (int n = 0; n <= ubound; n++) {
			//первый и последний элемент в строке равен 1
			C[n][0] = C[n][n] = BigInteger.ONE;
			//далее вычисляем внутреннюю часть
			for (int k = 1; k < n; k++) {
				C[n][k] = C[n-1][k-1].add(C[n-1][k]);//аналог C[n][k] = C[n-1][k-1] + C[n-1][k] для длинной арифметики
			}			
		}
	}

	//вывод треугольника в консоль
	public void show(){
		for (int n = 0; n <= ubound; n++) {
			for (int k = 0; k <= n; k++) {
				System.out.print(C[n][k] + " ");
			}
			System.out.println();
		}
	}

	//метод, возвращающий кол-во сочетаний из n по k
	public BigInteger get(int n,int k){
		return C[n][k];
	}
}
Пример использования:
BigPascalTriangle pas = new BigPascalTriangle(100);
pas.show();
System.out.println(pas.get(5, 3));//результат 10
Теперь о минусах, точнее минусе. Построение треугольника требует затрат оперативной памяти. Для массива long при n <= 66 - это неощутимо. Но для реализации с BigInteger - ощутимо, и при не хватке памяти будет выброшена ошибка:
java.lang.OutOfMemoryError: Java heap space
Ниже я приведу табличку с примерными ограничениями. Внимание! Данные приведены только с учетом построения треугольника, без учета того, что вы можете хранить в программе еще другие данные. Также данные могут отличаться, т.к. на приграничных значениях программа может то выбрасывать исключение, то не выбрасывать. Время построения треугольника тоже варьируется. Поэтому данные примерные!
Оперативная памятьДопустимое значениеВремя построения треугольника
256 МБn = 1700840 мс
512 МБn = 21501200 мс
1024 МБn = 29603700 мс
2048 МБn = 34707200 мс
4096 МБn = 434016000 мс
p.s. Треугольник Паскаля можно применять для решения и других задач. В заключении приведу цитату Мартина Гарднера о треугольнике Паскаля: Треугольник Паскаля так прост, что выписать его сможет даже десятилетний ребенок. В то же время он таит в себе неисчерпаемые сокровища и связывает воедино различные аспекты математики, не имеющие на первый взгляд между собой ничего общего. Столь необычные свойства позволяют считать треугольник Паскаля одной из наиболее изящных схем во всей математике.

Получение MD5 хэша на Java

Первый вариант (которым я пользуюсь):
public static final String md5(final String text){
	try{
		MessageDigest md = MessageDigest.getInstance("MD5");
		md.update(text.getBytes());
		String hash = new BigInteger(1, md.digest()).toString(16);
		while(hash.length() < 32) hash = "0" + hash;
		return hash;
	}catch(NoSuchAlgorithmException e){
		e.printStackTrace();
	}
	return "";
}
Второй вариант:
public static final String md5(final String s) {
    try {
        // Create MD5 Hash
        MessageDigest digest = java.security.MessageDigest
                .getInstance("MD5");
        digest.update(s.getBytes());
        byte messageDigest[] = digest.digest();

        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < messageDigest.length; i++) {
            String h = Integer.toHexString(0xFF & messageDigest[i]);
            while (h.length() < 2)
                h = "0" + h;
            hexString.append(h);
        }
        return hexString.toString();

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

Сортировка слиянием на Java

Реализация сортировки слиянием на Java:
//рекурсивная функция сортировки частей массива
public static int[] sort(int[] arr){
	if(arr.length < 2) return arr;
	int m = arr.length / 2;
	int[] arr1 = Arrays.copyOfRange(arr, 0, m);
	int[] arr2 = Arrays.copyOfRange(arr, m, arr.length);
	return merge(sort(arr1), sort(arr2));
}
//слияние двух массивов в один отсортированный
public static int[] merge(int[] arr1,int arr2[]){
	int n = arr1.length + arr2.length;
	int[] arr = new int[n];
	int i1=0;
	int i2=0;
	for(int i=0;i
Пример использования:
int[] arr ={2,1,0,6,1,9,8,7};//тестовый массив
System.out.println(Arrays.toString(arr));//вывод на экран исходного массива
int[] sortArr = sort(arr);//сортировка
System.out.println(Arrays.toString(sortArr));//вывод на экран отсортированного массива

Простые числа (Решето Эратосфена)

Нахождение простых чисел методом "Решето Эратосфена" Реализация на Java:
public class Primes {

	private boolean primes[];

	public Primes(int number){
		primes = new boolean[number+1];		
		Arrays.fill(primes, true);
		primes[0] = false;
		primes[1] = false;
		for(int i = 2;i*i < number;i++){
			if(primes[i]){
				for(int j=i*i;j < number;j+=i){
					primes[j] = false;
				}
			}
		}
		
	}
	
	public boolean check(int number){
		return primes[number];
	}

}
Применение:
int N = 100;
Primes primes = new Primes(N);
for(int i=0;i            

ArrayUtils — методы по работе с массивами

Методы по работе с массивами
public class ArrayUtils {

	//сумма элементов int массива
	public static int sum(int[] arr){
		int sum = 0;
		for(int i : arr) sum += i;
		return sum;
	}

	//сумма элементов long массива	
	public static long sum(long[] arr){
		long sum = 0;
		for(long l : arr) sum += l;
		return sum;
	}
	
	//сумма элементов double массива	
	public static double sum(double[] arr){
		double sum = 0;
		for(double d : arr) sum += d;
		return sum;
	}

	//максимальный элемент в int массива
	public static int max(int[] arr){
		int max = Integer.MIN_VALUE;
		for(int i : arr) if(i > max) max = i;
		return max;
	}
	
	//максимальный элемент в long массиве
	public static long max(long[] arr){
		long max = Long.MIN_VALUE;
		for(long l : arr) if(l > max) max = l;
		return max;
	}
	
	//максимальный элемент в double массиве
	public static double max(double[] arr){
		double max = Double.MIN_VALUE;
		for(double d : arr) if(d > max) max = d;
		return max;
	}

	//минимальный элемент в int массиве
	public static int min(int[] arr){
		int min = Integer.MAX_VALUE;
		for(int i : arr) if(i < min) min = i;
		return min;
	}
	
	//минимальный элемент в long массиве
	public static long min(long[] arr){
		long min = Long.MAX_VALUE;
		for(long l : arr) if(l < min) min = l;
		return min;
	}
	
	//минимальный элемент в double массиве
	public static double min(double[] arr){
		double min = Double.MAX_VALUE;
		for(double d : arr) if(d < min) min = d;
		return min;
	}

	//индекс максимального элемента в int массиве
	public static int maxIndex(int[] arr){
		int p = -1;
		int len = arr.length;
		if(len > 0){
			p = 0;
			for(int i = 1; i < len; i++) 
				if(arr[i] > arr[p]) 
					p = i;
		}
		return p;
	}
	
	//индекс максимального элемента в long массиве
	public static int maxIndex(long[] arr){
		int p = -1;
		int len = arr.length;
		if(len > 0){
			p = 0;
			for(int i = 1; i < len; i++)
				if(arr[i] > arr[p])
					p = i;
		}
		return p;
	}
	
	
	//индекс максимального элемента в double массиве
	public static int maxIndex(double[] arr){
		int p = -1;
		int len = arr.length;
		if(len > 0){
			p = 0;
			for(int i = 1; i < len; i++)
				if(arr[i] > arr[p])
					p = i;
		}
		return p;
	}

	//индекс минимального элемента в int массиве
	public static int minIndex(int[] arr){
		int p = -1;
		int len = arr.length;
		if(len > 0){
			p = 0;
			for(int i = 1; i < len; i++) 
				if(arr[i] < arr[p]) 
					p = i;
		}
		return p;
	}
	
	//индекс минимального элемента в long массиве
	public static int minIndex(long[] arr){
		int p = -1;
		int len = arr.length;
		if(len > 0){
			p = 0;
			for(int i = 1; i < len; i++)
				if(arr[i] < arr[p])
					p = i;
		}
		return p;
	}
	
	
	//индекс минимального элемента в double массиве
	public static int minIndex(double[] arr){
		int p = -1;
		int len = arr.length;
		if(len > 0){
			p = 0;
			for(int i = 1; i < len; i++)
				if(arr[i] < arr[p])
					p = i;
		}
		return p;
	}


}

FileUtils — Сборка методов по работе с файлами

Метод считывает все строки из переданного файла, добавляет их в List<String> и возвращает этот List
public static List getStringList(String fileName) throws IOException{
	BufferedReader br = null;
	List list = new ArrayList();
	String str;
	try{
		br = new BufferedReader(new FileReader(fileName));
		while( (str = br.readLine()) != null ){
			list.add(str);
		}
	}finally{
		if(br != null) br.close();
	}
	return list;
}

Получить все строки файла в виде List<String> (чтение в UTF-8)
public static List getFileLines(String fileName) throws IOException {
	List list = new ArrayList();
	BufferedReader br = null;
	try{
		br = new BufferedReader(
				new InputStreamReader(
						new FileInputStream(fileName),"UTF-8"));
		String str;
		while( (str = br.readLine()) != null ){
			list.add(str);
		}
	}finally {
		if(br != null)
			br.close();
	}
	return list;
}

Получение байтов файла по URL
public static byte[] downloadFile(URL url) throws IOException {
	byte[] bytes = null;
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	InputStream is = null;
	try {
		is = url.openStream();
		int ch;
		while( (ch = is.read()) != - 1){
			baos.write((byte)ch);
		}
		baos.flush();
		bytes = baos.toByteArray();
		return bytes;
	} finally {
		baos.close();
		if(is != null)is.close();
	}
}

Запись переданных строк в файл в кодировке UTF-8
public static void writeToFile(String fileName,List lines)
		throws IOException{
	BufferedWriter bw = new BufferedWriter(
		new OutputStreamWriter(new FileOutputStream(fileName),"UTF-8"));
	for(String str : lines){
		bw.append(str).append("\n");
	}
	bw.close();
}

Запись String, StringBuilder, StringBuffer в файл (UTF-8)
public static void writeToFile(String fileName,CharSequence charSequence)
		throws IOException{
	BufferedWriter bw = new BufferedWriter(
		new OutputStreamWriter(
			new FileOutputStream(fileName),"UTF-8"));
	bw.append(charSequence);
	bw.close();
}