Nên chọn kiểu viết code nào trong Java?

Số là bọn em đang làm bài tập. Đề bài cụ thể là: “Viết chương trình nhận vào một chuỗi bất kì và in ra chuỗi đó sau khi đã được viết hoa chữ cái đầu tiên mỗi từ. Nếu từ bắt đầu bằng kí tự đặc biệt thì từ đó không phải viết hoa kí tự nào. Không được dùng phương thức toUpperCase() có sẵn, hãy viết (các) phương thức chính để giải quyết vấn đề trên.”

Em viết như thế này:

public static boolean isInRange(char chr) {
	return ((int) chr >= 'a' && (int) chr <= 'z');
}

public static char uppercaseAChar(char chr) {
	if (isInRange(chr))
		return (char) (chr - 32);
	return chr;
}

public static String uppercaseEachWord(String str) {
	if (str == null || str.length() == 0)
		return str;
	StringBuilder res = new StringBuilder();
	res.append(uppercaseAChar(str.charAt(0)));
	for (int i = 1; i < str.length(); i++) {
		if (str.charAt(i - 1) == ' ') {
			res.append(uppercaseAChar(str.charAt(i)));
		} else
			res.append(str.charAt(i));
	}
	return res.toString();
}

Còn bạn em viết như thế này:

private static String inHoaCacChuDauTungTu(String s2) {
	char[] arr = s2.toCharArray();
	for (int i = 0; i < arr.length; i++) {
		if (i == 0 && (int) arr[i] >= 97 && (int) arr[i] <= 122) {
			arr[i] = (char) ((int) arr[i] - 32);
		}
		if ((int) arr[i] == 32) {
			if (i < arr.length - 1 && (int) arr[i + 1] >= 97 && (int) arr[i + 1] <= 122)
				arr[i + 1] = (char) ((int) arr[i + 1] - 32);
			if (i == arr.length - 1)
				break;
		}
	}
	String s = new String(arr);
	return s;
}

Thật sự thì khi test code của nó chạy nhanh hơn của em. Nhưng em thấy code của nó nhằng nhèo sao ấy, khó đọc quá. Theo các ACE, nên chọn cách viết code nào ạ? Có phải em chia ra thành các phương thức riêng lẻ nên nó bị chậm không? Mong các ACE giải đáp giúp. Em cảm ơn nhiều ạ!!!

2 Likes

Tùy , nếu chương trình chỉ chạy trên client thì nên để kiểu của bạn vì dễ maintain , nhưng nếu code một ứng dụng web có 1000 người dùng chẳng hạn , bạn nên để ý đến tốc độ và code kiểu thứ 2 , code phức tạp một chút , nhưng cố gắng comment thật dễ hiểu là ok . :smiley: code đẹp nhiều khi không phải là dễ đọc đâu , code đẹp là chạy hiệu quả sau đó thì đơn giản nhất có thể .

1 Like

Sao thầy em dạy StringBuilder nhanh hơn StringBuffer ạ?

em gà nhưng theo em biết thì đệ quy bao giờ cũng chạy chậm hơn vòng lặp

Code mình đâu có dùng đê quy đâu bạn.

vậy chắc gọi hàm thì chậm hơn :joy:

1 Like

StringBuffer is synchronized, StringBuilder is not.

2 Likes

Không phải sync nên nó nhanh hơn đúng không ạ?

StringBuffer thuộc loại synchronized , giúp xử lý đa luồng , có bộ đệm tránh xung đột . nếu không làm về đa luồng thì đừng có dùng buffer , không lại nặng code ra .

3 Likes

Code của bạn , khi chương trình complie đường đi sẽ loằng ngằng hơn khối có for vì thế chậm hơn là đúng rồi .

2 Likes

Mình thấy cách viết code của bạn dễ đọc hơn + có lợi về lâu dài. Giả sử sau này làm việc theo nhóm, hoặc 1 năm sau bạn quay trở lại đọc 2 đoạn code bên dưới, bạn nghĩ đoạn nào đọc dễ hiểu hơn? Dự án thực tế thường kéo dài nhiều năm (1-2 năm dev, 5+ năm bảo trì), nên 1 đoạn code qua tay nhiều người là chuyện bình thường.

Bạn nói đoạn code thứ 2 chạy nhanh hơn, nhưng nhanh hơn có đáng kể không? Theo mình thấy thì cả 2 đoạn này đều là O(n) hết, nhanh hơn không đáng kể thì nên bỏ qua (premature optimization is the root of all evil). Mình nghĩ nguyên nhân code của bạn chậm hơn là do thao tác trực tiếp trên String chứ không phải char[]. Bạn thử thay char[] arr = str.toCharArray(); xem tốc độ có cải thiện không? Bạn có thể dùng VisualVM để benchmark code, thấy chỗ nào chậm thì optimize chỗ đó

Về phần StringBuffer & StringBuilder, trong trường hợp này mình nghĩ nên dùng StringBuilder, tại vì theo như code ở trên, dùng cái nào thì khi chạy cũng chỉ có 1 thread.

  • StringBuffer thường sử dụng khi có nhiều thread dùng chung 1 object StringBuffer (chứ không phải nó tự tách ra 1 luồng riêng rồi chạy)
  • StringBuilder thì chỉ dùng nội bộ trong 1 thread, thread nào tạo thì thread đó dùng

Về cách code sao cho đẹp, dễ đọc. Bạn có thể đọc Clean Code, sách này viết ví dụ bằng Java nhưng có thể ứng dụng qua các ngôn ngữ khác.

Java Virtual Machine, lúc compile còn có một bước optimize bytecode nữa, nên chưa hẳn gọi nhiều method sẽ lâu hơn. Mình không rõ vụ optimize lắm nên cũng không dám chém :stuck_out_tongue:

4 Likes

cách 1 dễ đọc hơn.

cách 2 có 2 lần viết lại
arr[i] = (char) ((int) arr[i] - 32);
arr[i + 1] = (char) ((int) arr[i + 1] - 32);

(int) arr[i] >= 97 && (int) arr[i] <= 122
(int) arr[i + 1] >= 97 && (int) arr[i + 1] <= 122
nên tách thành 2 phương thức riêng chứ đừng copy paste thế này. Nếu copy paste thì khi debug phải tìm tất cả những chỗ đã copy paste mà sửa, rất vất vả. Nếu tách thành 1 phương thức riêng thì chỉ cần sửa phương thức này là xong.

ngoài ra
(int) arr[i] == 32
có lẽ nghiện C quá… mà C cũng ko bệnh thế này. arr[i] == ' ' dễ đọc hơn sao lại cast thành int rồi so sánh với 32 làm gì. Tương tự với >= 97 và <= 122. Khỏi cần cast về int. Viết arr[i] >= 'a' && arr[i] <= 'z' và gọn vừa dễ đọc.

ngoài ra nữa:
if (i == arr.length - 1) break;
ko cần thiết.

ngoài ra thì

String s = new String(arr);
return s;

viết luôn là return new String(arr); luôn cho gọn.

2 Likes

Viết theo cách của bạn là được rồi. Đừng cho một cái hàm ôm đồm quá nhiều thứ, sau này phải chỉnh sửa thì rất khó khăn…

2 Likes

Đồng ý với cách viết nên tách hàm ra cho dễ đọc hơn. Hơn thế nữa, để cuộc sống dễ dàng hơn thì tôi bổ sung ý kiến chúng ta nên tránh harded code. :sweat_smile:

  1. hàm isInRange(char chr) có thể thay bằng java.lang.Character.isLetter(char ch) sẽ bổ rẻ hơn. :yum:
  2. số 32 có thể thay bằng một constant, ví dụ: private static final int OFFSET = 'a' - 'A';
  3. kiểm tra kí tự khoảng trắng str.charAt(i - 1) == ' ' có thể thay bằng Character.isWhitespace(str.charAt(i - 1))

Bạn nghĩ sao? :slight_smile:

public class StringUtils {
	private static final int OFFSET = 'a' - 'A';
	private static final int FIRST_INDEX = 0;

	public static String upperCaseWords(String source) {
		char[] characters = source.toCharArray();
		
		for (int i = FIRST_INDEX; i < characters.length; i++) {
			if(Character.isLetter(characters[i]) 
				&& !Character.isUpperCase(characters[i])){
				if(i == FIRST_INDEX){
					convertToUpperCaseChar(characters, i);
				}else if(Character.isWhitespace(characters[i-1])){
					convertToUpperCaseChar(characters, i);
				}
			}
		}
		
		return new String(characters);
	}

	private static void convertToUpperCaseChar(char[] characters, int i) {
		characters[i] = (char) (characters[i] - OFFSET);
	}
}
3 Likes

Nếu nói là nên chọn kiểu nào thì hãy chọn kiểu này

Matcher m = Pattern.compile("([a-z])([a-z]*)"

=))))

3 Likes

Cảm ơn anh nhiều ạ! Code style chất quá. Em nhất định sẽ áp dụng.

1 Like

Xin anh vui lòng giải thích thêm :smile:

thêm điều kiện là nếu kí tự đã in hoa rồi thì không làm gì cả. Character.isUpperCase(characters[i])

2 Likes

Pattern- Regular Expressions

2 Likes

Cảm ơn anh, em lại được học thêm 1 thứ bổ ích.

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?