Cách đơn giản để lặp lại một chuỗi trong java


598

Tôi đang tìm kiếm một phương thức hoặc toán tử commons đơn giản cho phép tôi lặp lại một số Chuỗi n lần. Tôi biết tôi có thể viết điều này bằng cách sử dụng vòng lặp for, nhưng tôi muốn tránh các vòng lặp bất cứ khi nào cần thiết và một phương thức trực tiếp đơn giản nên tồn tại ở đâu đó.

String str = "abc";
String repeated = str.repeat(3);

repeated.equals("abcabcabc");

Có quan hệ với:

lặp lại chuỗi javascript Tạo NSString bằng cách lặp lại chuỗi khác một số lần nhất định

Đã chỉnh sửa

Tôi cố gắng tránh các vòng lặp khi chúng không hoàn toàn cần thiết bởi vì:

  1. Chúng thêm vào số dòng mã ngay cả khi chúng được giấu trong một chức năng khác.

  2. Ai đó đọc mã của tôi phải tìm ra những gì tôi đang làm trong vòng lặp đó. Ngay cả khi nó được bình luận và có tên biến có ý nghĩa, họ vẫn phải đảm bảo rằng nó không làm gì "thông minh".

  3. Các lập trình viên thích đặt những thứ thông minh vào các vòng lặp, ngay cả khi tôi viết nó thành "chỉ làm những gì nó dự định làm", điều đó không loại trừ ai đó đi cùng và thêm một số "sửa chữa" thông minh bổ sung.

  4. Họ rất thường dễ nhận sai. Đối với các vòng lặp liên quan đến các chỉ mục có xu hướng tạo ra bởi một lỗi.

  5. Đối với các vòng lặp thường sử dụng lại các biến giống nhau, làm tăng cơ hội thực sự khó tìm ra các lỗi phạm vi.

  6. Đối với các vòng lặp tăng số lượng địa điểm mà một thợ săn lỗi phải tìm.


35
Tôi hiểu rằng đối với các vòng lặp có thể gây ra một số vấn đề thực sự. Nhưng bạn không nên cố gắng tránh các vòng lặp "bằng mọi giá" bởi vì nếu bạn phải trả giá cho khả năng đọc, khả năng bảo trì và tốc độ thì bạn sẽ bị phản tác dụng. Đây là một trong những trường hợp đó.
Tưởng tượng

9
"Họ thêm vào số lượng dòng mã ngay cả khi chúng được giấu trong một chức năng khác" ... wow, chỉ là wow. Big-O, không phải LoC
Pyrolistic

7
@imagist Tôi đang tránh các vòng lặp trong các tình huống khiến tôi mất khả năng đọc và khả năng bảo trì. Tôi coi tốc độ là vấn đề ít quan trọng nhất ở đây (thực tế không phải là vấn đề). Tôi nghĩ rằng các vòng lặp được sử dụng quá mức và tôi đang cố gắng học cách chỉ sử dụng cho các vòng lặp khi chúng cần thiết và không phải là một giải pháp mặc định.
Ethan Heilman

3
@Pyrolistic Tôi không yêu cầu hiệu suất hoặc lợi ích tiệm cận. Thay vì nói rằng bằng cách viết ít mã hơn và sử dụng các chức năng của thư viện thay vì phát minh lại bánh xe, tôi giảm diện tích bề mặt lỗi (Dòng mã) trong khi tăng khả năng đọc. Cả hai điều tốt, tôi chắc chắn bạn sẽ đồng ý.
Ethan Heilman

4
@ e5; xin lỗi vì đã đăng nhiều năm sau. Tôi thấy câu hỏi này rất phù hợp. Nếu được chèn vào một phương thức, các đối số sẽ được kiểm tra (lần> = 0), lỗi được ném, v.v ... Điều này làm tăng tính mạnh mẽ nhưng cũng có thể đọc các dòng mã. Lặp lại một chuỗi là một cái gì đó không rõ ràng. Ai đọc mã sẽ biết chính xác chuỗi đó là gì. Ngay cả khi không có dòng nhận xét hoặc javadoc. Nếu chúng ta sử dụng một thư viện ổn định, sẽ hợp lý khi nghĩ rằng một hàm đơn giản không có lỗi, YET giới thiệu một số hình thức kiểm tra "sự mạnh mẽ" mà chúng ta thậm chí cần phải lo lắng. Nếu tôi có thể yêu cầu 10 cải tiến, thì (loại) này sẽ là một.
AgostinoX

Câu trả lời:


230

String::repeat

". ".repeat( 7 )  // Seven period-with-space pairs: . . . . . . . 

Điểm mới trong Java 11 là phương thức String::repeatthực hiện chính xác những gì bạn đã yêu cầu:

String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");

Javadoc của nó nói:

/**
 * Returns a string whose value is the concatenation of this
 * string repeated {@code count} times.
 * <p>
 * If this string is empty or count is zero then the empty
 * string is returned.
 *
 * @param count number of times to repeat
 *
 * @return A string composed of this string repeated
 * {@code count} times or the empty string if this
 * string is empty or count is zero
 *
 * @throws IllegalArgumentException if the {@code count} is
 * negative.
 *
 * @since 11
 */ 

2
@Nicolai mã nguồn cho nó, chỉ trong trường hợp ai đó quan tâm hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/aw
Eugene

7
Tôi thậm chí chưa thấy java 9 trên đường phố (và sẽ không hoạt động trong một thời gian dài ..) - và 11 dường như được thiết lập để vận chuyển ..
javadba

1
Có thể rõ ràng, nhưng bạn cũng có thể gọi phương thức này theo một chuỗi ký tự:"abc".repeat(3)
Boann

895

Đây là phiên bản ngắn nhất (bắt buộc phải có Java 1.5+):

repeated = new String(new char[n]).replace("\0", s);

Đâu nlà số lần bạn muốn lặp lại chuỗi và slà chuỗi cần lặp lại.

Không có nhập khẩu hoặc thư viện cần thiết.


22
Tôi không nghĩ nó bị xáo trộn chút nào. Các kiểu nguyên thủy ( char[]trong trường hợp này) được khởi tạo bằng null, sau đó a Stringđược tạo từ char[]và null là replaced()với ký tự bạn muốn trongs
Amarok

32
Mặc dù điều này rất thông minh (+1) Tôi nghĩ rằng nó khá nhiều chứng minh rằng điểm cho các vòng lặp thường tạo ra mã rõ ràng hơn
Richard Tingle

75
Đối với những người phàn nàn về obfuscation, khả năng đọc phụ thuộc vào kiến ​​thức. Điều này là hoàn toàn rõ ràng trong những gì nó làm và giáo dục cho những người có thể không nhìn thấy nó ngay lập tức. Đây là những gì bạn nhận được với Java bằng cách này.
dansalmo

11
Để có hiệu suất tốt hơn ...replace('\0', str)nên được sử dụng thay vì phiên bản String.
dùng686249

11
@ user686249: Chỉ có replace(char oldChar, char newChar)replace(CharSequence target, CharSequence replacement)vì vậy tôi không thấy nó có thể hoạt động như thế nào
user102008

334

Nếu bạn đang sử dụng Java <= 7 , thì đây là "súc tích" như được:

// create a string made up of n copies of string s
String.format("%0" + n + "d", 0).replace("0", s);

Trong Java 8 trở lên, có một cách dễ đọc hơn:

// create a string made up of n copies of string s
String.join("", Collections.nCopies(n, s));

Cuối cùng, đối với Java 11 trở lên, có một repeat​(int count)phương thức mới dành riêng cho điều này ( liên kết )

"abc".repeat(12);

Ngoài ra, nếu dự án của bạn sử dụng các thư viện java, có nhiều tùy chọn hơn.

Đối với Apache Commons :

StringUtils.repeat("abc", 12);

Đối với ổi Google :

Strings.repeat("abc", 12);

4
Cái trước gây ra một ngoại lệ khi nbằng không.
saka1029

Ví dụ java 8 không biên dịch -> Loại không khớp: không thể chuyển đổi từ Danh sách <Ký tự> sang CharSequence
Tôn giáo

6
@Arigion sphải là String, không phải làChar
Caner

@Caner Cảm ơn. Xấu của tôi, tôi xin lỗi. Rõ ràng hôm qua tôi đã quá mệt mỏi. Xin lỗi về downvote. Tôi sẽ xóa downvote ngay khi có thể (nó bị chặn cho đến khi câu hỏi được chỉnh sửa)
Tôn giáo

@Arigion không có vấn đề gì, hãy nói rõ rằng đây là một chuỗi
Caner

312

Commons Lang StringUtils.repeat ()

Sử dụng:

String str = "abc";
String repeated = StringUtils.repeat(str, 3);

repeated.equals("abcabcabc");

99
sử dụng một phụ thuộc một phương thức cho mục đích đơn giản trong thời gian dài có thể dẫn đến một địa ngục
dfa

81
Chắc chắn, ngoại trừ nó là lang. Tôi không nghĩ rằng tôi đã từng thấy một dự án hơn 5000 LỘC không có dấu phẩy.
Ethan Heilman

11
Commons Lang là mã nguồn mở - tải xuống và xem qua. Tất nhiên nó có một vòng lặp bên trong, nhưng nó không hoàn toàn đơn giản. Rất nhiều nỗ lực đã đi vào hồ sơ và tối ưu hóa việc thực hiện đó.
ChssPly76

28
Tôi không tránh các vòng lặp vì lý do hiệu suất (đọc lý do của tôi trong câu hỏi). Khi ai đó nhìn thấy StringUtils.repeat, họ biết tôi đang làm gì. Họ không phải lo lắng rằng tôi đã cố gắng viết phiên bản lặp lại của riêng mình và mắc lỗi. Nó là một đơn vị nhận thức nguyên tử!
Ethan Heilman

7
@ Thorbjørn Ravn Andersen - nó có thể nhận được nhiều điều thú vị hơn nếu mọi thứ tiếp tục được đưa ra khỏi bối cảnh
ChssPly76

140

Java 8 String.joincung cấp một cách gọn gàng để làm điều này kết hợp với Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));

7
cảm ơn bạn! Đối với Android TextUtils.join () có thể được sử dụng thay cho String.join ()
MinaHany

2
Cảm ơn bạn cho câu trả lời này. Đây dường như là cách sạch nhất mà không cần sử dụng bất kỳ phương pháp tiện ích API oder bên ngoài nào! rất tốt!!
Andreas M. Oberheim

2
Điều hay ho của phương pháp này là với phép nối, bạn có thể cung cấp một ký tự phân tách rất tiện dụng nếu bạn đang xây dựng một danh sách CSV. Với tất cả các phương thức khác, bạn có một ký tự nối kết thúc cần được loại bỏ trong một hoạt động riêng biệt.
DroidOS

102

Đây là một cách để làm điều đó bằng cách chỉ sử dụng các hàm String tiêu chuẩn và không có vòng lặp rõ ràng:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);

5
Thật tuyệt vời :-) Mặc dù hãy cẩn thận khi trở thành số 0!
Yang Meyer

4
@Vijay Dev & fortran: Không, ý anh là vậy replace(). Trong Java 1.5+, có một phiên bản quá tải replace(), mất hai CharSequences (bao gồm Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/
trộm

79
Ôi. Điều này thật xấu xí.
Karel Bílek

8
@mzuba hãy nói n=3: đầu tiên nó định dạng một chuỗi để trông giống như %03d( %%là để thoát dấu phần trăm), đó là mã định dạng để thêm 3 số 0 đệm, sau đó định dạng 0bằng chuỗi đó, dẫn đến 000và cuối cùng thay thế từng 0chuỗi bằng chuỗi
fortran

15
Bạn có thể làm cho giải pháp bớt xấu xí và dễ hiểu hơn: String.format ("% 0" + n + "d", 0) .replace ("0", s)
Artur

87

Nếu bạn giống tôi và muốn sử dụng Google Guava chứ không phải Apache Commons. Bạn có thể sử dụng phương thức lặp lại trong lớp Chuỗi Guava.

Strings.repeat("-", 60);

2
... và nhận được 3Mb phụ thuộc mới.
MonoThreaded

6
@MonoThreaded Tôi nghĩ rằng nó sẽ đi mà không nói, nhưng đừng bao gồm ổi chỉ để lặp lại chuỗi. Câu trả lời của tôi là về việc bạn đã sử dụng ổi hay chưa thì đây là cách bạn làm.
Jack

53

Với , bạn cũng có thể sử dụng Stream.generate.

import static java.util.stream.Collectors.joining;
...
String repeated = Stream.generate(() -> "abc").limit(3).collect(joining()); //"abcabcabc"

và bạn có thể gói nó trong một phương thức tiện ích đơn giản nếu cần:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}

6
... hoặc return IntStream.range(0, times).mapToObj(i -> str).collect(joining());song song tốt hơn
Alexis C.

32

Vì vậy, bạn muốn tránh vòng lặp?

Ở đây bạn có nó:

public static String repeat(String s, int times) {
    if (times <= 0) return "";
    else return s + repeat(s, times-1);
}

(tất nhiên tôi biết điều này là xấu và không hiệu quả, nhưng nó không có vòng lặp :-p)

Bạn muốn nó đơn giản và đẹp hơn? sử dụng jython:

s * 3

Chỉnh sửa : hãy tối ưu hóa nó một chút :-D

public static String repeat(String s, int times) {
   if (times <= 0) return "";
   else if (times % 2 == 0) return repeat(s+s, times/2);
   else return s + repeat(s+s, times/2);
}

Edit2 : Tôi đã thực hiện một điểm chuẩn nhanh và bẩn cho 4 lựa chọn thay thế chính, nhưng tôi không có thời gian để chạy nó nhiều lần để lấy phương tiện và vẽ thời gian cho một số đầu vào ... Vì vậy, đây là mã nếu có ai muốn để thử nó:

public class Repeat {
    public static void main(String[] args)  {
        int n = Integer.parseInt(args[0]);
        String s = args[1];
        int l = s.length();
        long start, end;

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatLog2(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("RecLog2Concat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatR(s,i).length()!=i*l) throw new RuntimeException();
        }               
        end = System.currentTimeMillis();
        System.out.println("RecLinConcat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatIc(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("IterConcat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatSb(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("IterStrB: " + (end-start) + "ms");
    }

    public static String repeatLog2(String s, int times) {
        if (times <= 0) {
            return "";
        }
        else if (times % 2 == 0) {
            return repeatLog2(s+s, times/2);
        }
        else {
           return s + repeatLog2(s+s, times/2);
        }
    }

    public static String repeatR(String s, int times) {
        if (times <= 0) {
            return "";
        }
        else {
            return s + repeatR(s, times-1);
        }
    }

    public static String repeatIc(String s, int times) {
        String tmp = "";
        for (int i = 0; i < times; i++) {
            tmp += s;
        }
        return tmp;
    }

    public static String repeatSb(String s, int n) {
        final StringBuilder sb = new StringBuilder();
        for(int i = 0; i < n; i++) {
            sb.append(s);
        }
        return sb.toString();
    }
}

Phải có 2 đối số, đầu tiên là số lần lặp (mỗi hàm chạy với số lần lặp lại arg từ 1..n) và lần thứ hai là chuỗi lặp lại.

Cho đến nay, việc kiểm tra nhanh thời gian chạy với các đầu vào khác nhau để lại thứ hạng như thế này (tốt hơn đến tệ hơn):

  1. Lặp lại StringBuilder bổ sung (1x).
  2. Các đệ trình log2 nối ghép đệ quy (~ 3x).
  3. Đệ trình tuyến tính nối đệ quy (~ 30x).
  4. Lặp lại tuyến tính nối tuyến tính (~ 45x).

Tôi sẽ không bao giờ đoán rằng hàm đệ quy nhanh hơn forvòng lặp: -o

Hãy vui vẻ (ctional xD).


1
+1 cho đệ quy và rõ ràng là một hacker không thể thiếu. Tôi cũng không nghĩ rằng điều này là không hiệu quả, nối chuỗi không phải là chiến tranh như trước đây, bởi vì + thực sự chỉ là một UTH của StringBuilder. Xem stackoverflow.com/questions/47605/java-opes-concatenationschneide.wordpress.com/2009/02/23/ . Tôi tự hỏi bao nhiêu tất cả các ngăn xếp đẩy và bật ra từ chi phí đệ quy, hoặc nếu hotspot chăm sóc chúng. Thực sự muốn tôi có thời gian rảnh để điểm chuẩn nó. Một người khác có thể?
Ethan Heilman

@ e5: fortran đúng; giải pháp này có thể được thực hiện hiệu quả hơn. Việc triển khai này sẽ không cần thiết tạo một StringBuilder mới (và một Chuỗi mới) cho mỗi lần đệ quy. Vẫn là một giải pháp tốt đẹp.
cướp

3
@ e5 Tôi ước mình là một hacker Lisp xD ... Nếu là tôi, tôi đã sử dụng chức năng đệ quy đuôi :-p
fortran

1
Microbenchmark không hoạt động tốt trong Java. Cố gắng đo tốc độ thực hiện của bạn như thế là không tốt.
ceklock

@tecnotron Tôi biết, nhưng họ vẫn tốt hơn không có gì ... Và điều 'bất ngờ' duy nhất là sự khác biệt nhỏ giữa phép nối vòng lặp ngây thơ và đệ quy tuyến tính.
fortran

20

Điều này chứa ít ký tự hơn câu hỏi của bạn

public static String repeat(String s, int n) {
    if(s == null) {
        return null;
    }
    final StringBuilder sb = new StringBuilder(s.length() * n);
    for(int i = 0; i < n; i++) {
        sb.append(s);
    }
    return sb.toString();
}

4
Nó chứa nhiều ký tự hơn câu trả lời của tôi StringUtils.repeat (str, n).
Ethan Heilman

8
Trừ khi bạn đã sử dụng Apache Commons, câu trả lời này ít rắc rối hơn - không tải xuống thư viện khác, kể cả trong đường dẫn lớp của bạn, đảm bảo giấy phép của nó tương thích với bạn, v.v.
Paul Tomblin

7
Xin vui lòng, không bao giờ trả về null - trong trường hợp đó trả về một chuỗi rỗng, cho phép bạn luôn luôn sử dụng giá trị trả về không được kiểm tra. Nếu không, những gì tôi muốn giới thiệu các poster để sử dụng.
Thorbjørn Ravn Andersen

7
Vâng, có ba cách để xử lý nếu s là null. 1. Truyền lỗi (trả về null), 2. Ẩn lỗi (trả về ""), 3. Ném NPE. Che giấu lỗi và ném NPE không hay, vì vậy tôi đã thông qua lỗi.
Pyrolistic

1
@EthanHeilman thêm giá trị 2MB commons-lang3.3.1-sourcesvà bạn không còn tốt nữa;) Nhưng nếu ai đó đã có commons-lang, tôi ủng hộ câu trả lời của bạn.
TWiStErRob

9

dựa trên câu trả lời của fortran , đây là phiên bản có thể sử dụng StringBuilder:

public static void repeat(StringBuilder stringBuilder, String s, int times) {
    if (times > 0) {
        repeat(stringBuilder.append(s), s, times - 1);
    }
}

public static String repeat(String s, int times) {
    StringBuilder stringBuilder = new StringBuilder(s.length() * times);
    repeat(stringBuilder, s, times);
    return stringBuilder.toString();
}

1
lặp đi lặp lại thay vì đệ quy sẽ làm giảm # khung stack cho số lần lặp lớn.
La-comadreja

7

sử dụng Dollar rất đơn giản như gõ:

@Test
public void repeatString() {
    String string = "abc";
    assertThat($(string).repeat(3).toString(), is("abcabcabc"));
}

PS: lặp lại hoạt động cũng cho mảng, Danh sách, Bộ, vv


3
phương thức assertThat () có thực sự cần thiết?
ceklock

7

Tôi muốn một hàm để tạo một danh sách các dấu hỏi được phân cách bằng dấu phẩy cho các mục đích JDBC và tìm thấy bài đăng này. Vì vậy, tôi quyết định thực hiện hai biến thể và xem cái nào hoạt động tốt hơn. Sau 1 triệu lần lặp, StringBuilder trong khu vườn mất 2 giây (fun1) và phiên bản được cho là tối ưu hơn (fun2) mất 30 giây. Điểm khó hiểu là gì?

private static String fun1(int size) {
    StringBuilder sb = new StringBuilder(size * 2);
    for (int i = 0; i < size; i++) {
        sb.append(",?");
    }
    return sb.substring(1);
}

private static String fun2(int size) {
    return new String(new char[size]).replaceAll("\0", ",?").substring(1);
}

3
Tôi có ý nghĩa rằng cái thứ hai sẽ mất nhiều thời gian hơn. Nó đang thực hiện tìm kiếm chuỗi và sau đó sửa đổi ký tự chuỗi theo ký tự.
Ethan Heilman

7

Giải pháp OOP

Gần như mọi câu trả lời đều đề xuất một hàm tĩnh như một giải pháp, nhưng suy nghĩ Hướng đối tượng (cho mục đích tái sử dụng và rõ ràng) Tôi đã đưa ra Giải pháp thông qua Phân quyền thông qua Giao diện CharSequence (cũng mở ra khả năng sử dụng trên các Lớp CharSequence có thể thay đổi).

Lớp sau có thể được sử dụng có hoặc không có Dấu phân tách / Chuỗi / CharSequence và mỗi lệnh gọi "toString ()" sẽ tạo chuỗi lặp lại cuối cùng. Đầu vào / Dấu tách không chỉ giới hạn ở Lớp Chuỗi, mà còn có thể là mọi Lớp thực hiện CharSequence (ví dụ StringBuilder, StringBuffer, v.v.)!

Mã nguồn:

/**
 * Helper-Class for Repeating Strings and other CharSequence-Implementations
 * @author Maciej Schuttkowski
 */
public class RepeatingCharSequence implements CharSequence {
    final int count;
    CharSequence internalCharSeq = "";
    CharSequence separator = "";
    /**
     * CONSTRUCTOR - RepeatingCharSequence
     * @param input CharSequence to repeat
     * @param count Repeat-Count
     */
    public RepeatingCharSequence(CharSequence input, int count) {
        if(count < 0)
            throw new IllegalArgumentException("Can not repeat String \""+input+"\" less than 0 times! count="+count);
        if(count > 0)
            internalCharSeq = input;
        this.count = count;
    }
    /**
     * CONSTRUCTOR - Strings.RepeatingCharSequence
     * @param input CharSequence to repeat
     * @param count Repeat-Count
     * @param separator Separator-Sequence to use
     */
    public RepeatingCharSequence(CharSequence input, int count, CharSequence separator) {
        this(input, count);
        this.separator = separator;
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        checkBounds(start);
        checkBounds(end);
        int subLen = end - start;
        if (subLen < 0) {
            throw new IndexOutOfBoundsException("Illegal subSequence-Length: "+subLen);
        }
        return (start == 0 && end == length()) ? this
                    : toString().substring(start, subLen);
    }
    @Override
    public int length() {
        //We return the total length of our CharSequences with the separator 1 time less than amount of repeats:
        return count < 1 ? 0
                : ( (internalCharSeq.length()*count) + (separator.length()*(count-1)));
    }
    @Override
    public char charAt(int index) {
        final int internalIndex = internalIndex(index);
        //Delegate to Separator-CharSequence or Input-CharSequence depending on internal index:
        if(internalIndex > internalCharSeq.length()-1) {
            return separator.charAt(internalIndex-internalCharSeq.length());
        }
        return internalCharSeq.charAt(internalIndex);
    }
    @Override
    public String toString() {
        return count < 1 ? ""
                : new StringBuilder(this).toString();
    }

    private void checkBounds(int index) {
        if(index < 0 || index >= length())
            throw new IndexOutOfBoundsException("Index out of Bounds: "+index);
    }
    private int internalIndex(int index) {
        // We need to add 1 Separator-Length to total length before dividing,
        // as we subtracted one Separator-Length in "length()"
        return index % ((length()+separator.length())/count);
    }
}

Cách sử dụng-Ví dụ:

public static void main(String[] args) {
    //String input = "12345";
    //StringBuffer input = new StringBuffer("12345");
    StringBuilder input = new StringBuilder("123");
    //String separator = "<=>";
    StringBuilder separator = new StringBuilder("<=");//.append('>');
    int repeatCount = 2;

    CharSequence repSeq = new RepeatingCharSequence(input, repeatCount, separator);
    String repStr = repSeq.toString();

    System.out.println("Repeat="+repeatCount+"\tSeparator="+separator+"\tInput="+input+"\tLength="+input.length());
    System.out.println("CharSeq:\tLength="+repSeq.length()+"\tVal="+repSeq);
    System.out.println("String :\tLength="+repStr.length()+"\tVal="+repStr);

    //Here comes the Magic with a StringBuilder as Input, as you can append to the String-Builder
    //and at the same Time your Repeating-Sequence's toString()-Method returns the updated String :)
    input.append("ff");
    System.out.println(repSeq);
    //Same can be done with the Separator:
    separator.append("===").append('>');
    System.out.println(repSeq);
}

Ví dụ-Đầu ra:

Repeat=2    Separator=<=    Input=123   Length=3
CharSeq:    Length=8    Val=123<=123
String :    Length=8    Val=123<=123
123ff<=123ff
123ff<====>123ff

4
hiếm khi tôi thấy những thứ kinh tởm: /
Ven

6

chỉ sử dụng các lớp JRE ( System.arraycopy ) và cố gắng giảm thiểu số lượng đối tượng tạm thời mà bạn có thể viết một cái gì đó như:

public static String repeat(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    final int length = toRepeat.length();
    final int total = length * times;
    final char[] src = toRepeat.toCharArray();
    char[] dst = new char[total];

    for (int i = 0; i < total; i += length) {
        System.arraycopy(src, 0, dst, i, length);
    }

    return String.copyValueOf(dst);
}

BIÊN TẬP

và không có vòng lặp bạn có thể thử với:

public static String repeat2(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    String[] copies = new String[times];
    Arrays.fill(copies, toRepeat);
    return Arrays.toString(copies).
              replace("[", "").
              replace("]", "").
              replaceAll(", ", "");
}

CHỈNH SỬA 2

sử dụng Bộ sưu tập thậm chí còn ngắn hơn:

public static String repeat3(String toRepeat, int times) {
    return Collections.nCopies(times, toRepeat).
           toString().
           replace("[", "").
           replace("]", "").
           replaceAll(", ", "");
}

Tuy nhiên tôi vẫn thích phiên bản đầu tiên.


6
-1: quá thông minh bằng một nửa. Nếu mục đích của bạn là làm cho mã của bạn dễ đọc hoặc hiệu quả, thì những "giải pháp" này không phải là một ý tưởng hay. 'Lặp lại' đơn giản có thể được viết lại bằng StringBuilder (đặt công suất ban đầu). Và 'repeat2' / 'repeat3' thực sự không hiệu quả và phụ thuộc vào cú pháp không xác định của Chuỗi được tạo bởi String []. ToString ().
Stephen C

@Thorb: hoàn toàn, với mã này, bạn không thể sử dụng "metacharacter", [],
dfa

@Stephen: câu hỏi đã được chỉnh sửa để yêu cầu rõ ràng không có vòng lặp. Một câu trả lời dựa trên StringBuilder đã được cung cấp vì vậy tôi đã tránh đăng một bản sao
dfa

@Stephan: Tôi không thể tìm ra downvote. Câu trả lời chỉnh sửa của tôi là vòng lặp miễn phí như được yêu cầu. Không có yêu cầu về hiệu quả. Tôi nghĩ rằng câu hỏi này chỉ là một nỗ lực trí tuệ để tạo ra sự kết hợp không có vòng lặp.
dfa

@Stephan: String được sản xuất thông qua Collection.toString (và Arrays.toString) đều được quy định rõ ràng trong AbstractCollection.toString: "Các đại diện chuỗi bao gồm một danh sách các yếu tố của bộ sưu tập theo thứ tự chúng được trả về bởi iterator của nó, kèm theo trong dấu ngoặc vuông ( "[]"). Các phần tử liền kề được phân tách bằng các ký tự "," (dấu phẩy và dấu cách). "
dfa

6

Không phải là ngắn nhất, nhưng (tôi nghĩ) cách nhanh nhất là sử dụng StringBuilder:

 /**
   * Repeat a String as many times you need.
   *
   * @param i - Number of Repeating the String.
   * @param s - The String wich you want repeated.
   * @return The string n - times.
   */
  public static String repeate(int i, String s) {
    StringBuilder sb = new StringBuilder();
    for (int j = 0; j < i; j++)
      sb.append(s);
    return sb.toString();
  }

5

Nếu tốc độ là mối quan tâm của bạn, thì bạn nên sử dụng càng ít sao chép bộ nhớ càng tốt. Do đó, nó là cần thiết để làm việc với các mảng ký tự.

public static String repeatString(String what, int howmany) {
    char[] pattern = what.toCharArray();
    char[] res = new char[howmany * pattern.length];
    int length = pattern.length;
    for (int i = 0; i < howmany; i++)
        System.arraycopy(pattern, 0, res, i * length, length);
    return new String(res);
}

Để kiểm tra tốc độ, một phương pháp tối ưu tương tự sử dụng StirngBuilder giống như sau:

public static String repeatStringSB(String what, int howmany) {
    StringBuilder out = new StringBuilder(what.length() * howmany);
    for (int i = 0; i < howmany; i++)
        out.append(what);
    return out.toString();
}

và mã để kiểm tra nó:

public static void main(String... args) {
    String res;
    long time;

    for (int j = 0; j < 1000; j++) {
        res = repeatString("123", 100000);
        res = repeatStringSB("123", 100000);
    }

    time = System.nanoTime();
    res = repeatString("123", 1000000);
    time = System.nanoTime() - time;
    System.out.println("elapsed repeatString: " + time);

    time = System.nanoTime();
    res = repeatStringSB("123", 1000000);
    time = System.nanoTime() - time;
    System.out.println("elapsed repeatStringSB: " + time);

}

Và đây là kết quả chạy từ hệ thống của tôi:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Lưu ý rằng kiểm tra vòng lặp là để đá trong JIT và có kết quả tối ưu.


4

vì mục đích dễ đọc và tính di động:

public String repeat(String str, int count){
    if(count <= 0) {return "";}
    return new String(new char[count]).replace("\0", str);
}

3

Nếu bạn lo lắng về hiệu suất, chỉ cần sử dụng StringBuilder bên trong vòng lặp và thực hiện .toString () khi thoát khỏi Loop. Heck, viết Lớp Util của riêng bạn và sử dụng lại. 5 dòng mã tối đa.


2

Tôi thực sự thích câu hỏi này. Có rất nhiều kiến ​​thức và phong cách. Vì vậy, tôi không thể rời khỏi nó mà không hiển thị rock and roll của mình;)

{
    String string = repeat("1234567890", 4);
    System.out.println(string);
    System.out.println("=======");
    repeatWithoutCopySample(string, 100000);
    System.out.println(string);// This take time, try it without printing
    System.out.println(string.length());
}

/**
 * The core of the task.
 */
@SuppressWarnings("AssignmentToMethodParameter")
public static char[] repeat(char[] sample, int times) {
    char[] r = new char[sample.length * times];
    while (--times > -1) {
        System.arraycopy(sample, 0, r, times * sample.length, sample.length);
    }
    return r;
}

/**
 * Java classic style.
 */
public static String repeat(String sample, int times) {
    return new String(repeat(sample.toCharArray(), times));
}

/**
 * Java extreme memory style.
 */
@SuppressWarnings("UseSpecificCatch")
public static void repeatWithoutCopySample(String sample, int times) {
    try {
        Field valueStringField = String.class.getDeclaredField("value");
        valueStringField.setAccessible(true);
        valueStringField.set(sample, repeat((char[]) valueStringField.get(sample), times));
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

Bạn có thích nó không?


1
Trong thử nghiệm cực đoan hơn của mình, tôi tạo ra độ dài lặp lại chuỗi 1.700.000.000 (1.7 gigas) ,, sử dụng -Xms4937m
Daniel De León

2
public static String repeat(String str, int times) {
    int length = str.length();
    int size = length * times;
    char[] c = new char[size];
    for (int i = 0; i < size; i++) {
        c[i] = str.charAt(i % length);
    }
    return new String(c);
}

2

Vòng lặp đơn giản

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}

2
chuyển timesđến hàm tạo StringBuilder.
Behrouz.M

2

Thử thứ này đi:

public static char[] myABCs = {'a', 'b', 'c'};
public static int numInput;
static Scanner in = new Scanner(System.in);

public static void main(String[] args) {
    System.out.print("Enter Number of Times to repeat: ");
    numInput = in.nextInt();
    repeatArray(numInput);
}

public static int repeatArray(int y) {
    for (int a = 0; a < y; a++) {
        for (int b = 0; b < myABCs.length; b++) {
            System.out.print(myABCs[b]);                
        }
        System.out.print(" ");
    }
    return y;
}

2

Sử dụng đệ quy, bạn có thể thực hiện các thao tác sau (sử dụng toán tử ternary, tối đa một dòng):

public static final String repeat(String string, long number) {
    return number == 1 ? string : (number % 2 == 0 ? repeat(string + string, number / 2) : string + repeat(string + string, (number - 1) / 2));
}

Tôi biết, nó xấu và có thể không hiệu quả, nhưng đó là một dòng!


Đây là cách tiếp cận tôi sẽ thực hiện nhưng tại sao phải kiểm tra nhiều hơn mức cần thiết? trả về số> 0? chuỗi + lặp lại (chuỗi, số-1): "";
Fering

Ồ, có vẻ như niczm25 đã trả lời với nó bên dưới
Fering

@Fering lý do chính sao cho cách này là trung bình O (log N) chứ không phải O (N) luôn. Tối ưu hóa hơn một chút so với cái khác, mặc dù vậy vẫn còn xấu.
HyperNeutrino

2

một giải pháp một dòng đơn giản:
yêu cầu Java 8

Collections.nCopies( 3, "abc" ).stream().collect( Collectors.joining() );

1

Mặc dù bạn không muốn sử dụng các vòng lặp, tôi nghĩ bạn nên sử dụng một vòng lặp.

String repeatString(String s, int repetitions)
{
    if(repetitions < 0) throw SomeException();

    else if(s == null) return null;

    StringBuilder stringBuilder = new StringBuilder(s.length() * repetitions);

    for(int i = 0; i < repetitions; i++)
        stringBuilder.append(s);

    return stringBuilder.toString();
}

Lý do của bạn không sử dụng vòng lặp for là không tốt. Đáp lại những lời chỉ trích của bạn:

  1. Bất cứ giải pháp nào bạn sử dụng sẽ gần như chắc chắn sẽ dài hơn thế này. Sử dụng một chức năng dựng sẵn chỉ kéo nó dưới nhiều vỏ bọc hơn.
  2. Ai đó đang đọc mã của bạn sẽ phải tìm ra những gì bạn đang làm trong vòng lặp không phải là vòng lặp đó. Cho rằng một vòng lặp for là cách thành ngữ để làm điều này, sẽ dễ dàng hơn nhiều nếu bạn thực hiện nó với một vòng lặp for.
  3. Có ai đó có thể thêm một cái gì đó thông minh, nhưng bằng cách tránh một vòng lặp for, bạn đang làm một cái gì đó thông minh . Điều đó giống như tự bắn vào chân mình một cách cố ý để tránh tự bắn vào chân mình.
  4. Các lỗi khác nhau cũng rất dễ mắc phải chỉ với một bài kiểm tra. Cho rằng bạn nên kiểm tra mã của mình, một lỗi nhỏ sẽ dễ dàng sửa và bắt. Và điều đáng chú ý: đoạn mã trên không có lỗi do lỗi. Đối với các vòng lặp là dễ dàng như nhau để có được đúng.
  5. Vì vậy, không sử dụng lại các biến. Đó không phải là lỗi của vòng lặp for.
  6. Một lần nữa, bất cứ giải pháp nào bạn sử dụng. Và như tôi đã lưu ý trước đó; một thợ săn lỗi có thể sẽ mong đợi bạn làm điều này với vòng lặp for, vì vậy họ sẽ dễ dàng tìm thấy nó hơn nếu bạn sử dụng vòng lặp for.

3
-1. Đây là hai bài tập cho bạn: a) chạy mã của bạn với repetitions = -5. b) tải xuống Commons Lang và chạy repeatString('a', 1000)một triệu lần trong một vòng lặp; làm tương tự với mã của bạn; so sánh thời đại. Đối với tín dụng thêm làm tương tự với repeatString('ab', 1000).
ChssPly76

2
Bạn đang tranh luận rằng mã của bạn dễ đọc hơn sau đó StringUtils.repeat("ab",1000)? Bởi vì đó là câu trả lời của tôi mà bạn đã đánh giá thấp. Nó cũng thực hiện tốt hơn và không có lỗi.
ChssPly76

2
Đọc câu thứ 2 trong câu hỏi bạn đang trích dẫn. "Tôi cố gắng tránh các vòng lặp bằng mọi giá vì" đã được thêm vào câu hỏi như một sự làm rõ để trả lời câu trả lời của Andrew Hare sau câu trả lời của tôi - không phải là vấn đề vì nếu vị trí bạn đang đảm nhận là "câu trả lời là xấu nếu vòng lặp là được sử dụng ở bất cứ đâu "không có câu trả lời cho câu hỏi OP. Ngay cả các giải pháp của dfa - sáng tạo như chúng - sử dụng cho các vòng lặp bên trong. "địa ngục jar" đã được trả lời ở trên; commons lang được sử dụng trong mọi ứng dụng có kích thước phù hợp và do đó không thêm phụ thuộc mới.
ChssPly76

2
@ ChssPly76 tại thời điểm này tôi khá chắc chắn rằng nhà tưởng tượng đang troll. Tôi thực sự có một thời gian khó khăn để xem làm thế nào bất cứ ai có thể đọc những gì tôi đã viết và nghiêm túc nghĩ rằng các phản hồi được gõ ở trên.
Ethan Heilman

1
@ ChssPly76 câu trả lời của tôi không có bất kỳ vòng lặp nào :-p
fortran

1

Nếu bạn chỉ biết độ dài của chuỗi đầu ra (và nó có thể không chia hết cho độ dài của chuỗi đầu vào), thì hãy sử dụng phương pháp này:

static String repeat(String s, int length) {
    return s.length() >= length ? s.substring(0, length) : repeat(s + s, length);
}

Bản demo sử dụng:

for (int i = 0; i < 50; i++)
    System.out.println(repeat("_/‾\\", i));

Không sử dụng với khoảng trống slength> 0, vì không thể có được kết quả mong muốn trong trường hợp này.


0

đây là Stringutils.java StringUtils.java mới nhất

    public static String repeat(String str, int repeat) {
    // Performance tuned for 2.0 (JDK1.4)

    if (str == null) {
        return null;
    }
    if (repeat <= 0) {
        return EMPTY;
    }
    int inputLength = str.length();
    if (repeat == 1 || inputLength == 0) {
        return str;
    }
    if (inputLength == 1 && repeat <= PAD_LIMIT) {
        return repeat(str.charAt(0), repeat);
    }

    int outputLength = inputLength * repeat;
    switch (inputLength) {
        case 1 :
            return repeat(str.charAt(0), repeat);
        case 2 :
            char ch0 = str.charAt(0);
            char ch1 = str.charAt(1);
            char[] output2 = new char[outputLength];
            for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
                output2[i] = ch0;
                output2[i + 1] = ch1;
            }
            return new String(output2);
        default :
            StringBuilder buf = new StringBuilder(outputLength);
            for (int i = 0; i < repeat; i++) {
                buf.append(str);
            }
            return buf.toString();
    }
    }

nó thậm chí không cần phải lớn như vậy, có thể được tạo thành cái này và có thể được sao chép và dán vào một lớp tiện ích trong dự án của bạn.

    public static String repeat(String str, int num) {
    int len = num * str.length();
    StringBuilder sb = new StringBuilder(len);
    for (int i = 0; i < times; i++) {
        sb.append(str);
    }
    return sb.toString();
    }

Vì vậy, e5, tôi nghĩ cách tốt nhất để làm điều này sẽ chỉ đơn giản là sử dụng mã được đề cập ở trên, hoặc bất kỳ câu trả lời nào ở đây. nhưng commons lang chỉ là quá lớn nếu đó là một dự án nhỏ


Tôi không nghĩ có nhiều thứ khác bạn có thể làm ... ngoại trừ có thể là AOT !!
alexmherrmann

0

Tôi đã tạo ra một phương thức đệ quy làm cùng một thứ bạn muốn .. cứ thoải mái sử dụng ...

public String repeat(String str, int count) {
    return count > 0 ?  repeat(str, count -1) + str: "";
}

tôi có cùng một câu trả lời về Tôi có thể nhân chuỗi trong java để lặp lại trình tự không?


Không cần phân bổ lại chuỗi, và đệ quy trên đầu ... xấu, xấu, không tốt.
Alexander - Hồi phục lại

1
Điều này sẽ chậm. Không được khuyến khích! Sử dụng StringBuilderthay thế.
Svetlin Nakov

0
public static String rep(int a,String k)

       {
           if(a<=0)
                return "";
           else 
           {a--;
               return k+rep(a,k);
       }

Bạn có thể sử dụng phương pháp đệ quy này cho mục tiêu mong muốn của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.