Lần lặp cuối cùng của vòng lặp nâng cao trong java


140

Có cách nào để xác định xem vòng lặp có lặp lại lần cuối không. Mã của tôi trông giống như thế này:

int[] array = {1, 2, 3...};
StringBuilder builder = new StringBuilder();

for(int i : array)
{
    builder.append("" + i);
    if(!lastiteration)
        builder.append(",");
}

Bây giờ vấn đề là tôi không muốn thêm dấu phẩy trong lần lặp cuối cùng. Bây giờ có một cách để xác định xem đó là lần lặp cuối cùng hay tôi bị mắc kẹt với vòng lặp for hoặc sử dụng bộ đếm bên ngoài để theo dõi.


1
Vâng! Thật buồn cười, tôi chỉ muốn hỏi chính xác cùng một câu hỏi!
PhiLho

Các loại câu hỏi tương tự trả về (và sẽ như vậy). Bây giờ tại sao bạn muốn tạo một vòng lặp nếu một yếu tố cần một cách xử lý khác? stackoverflow.com/questions/156650/
Mạnh

Vì bạn có một mảng cố định, tại sao sử dụng nâng cao cho? for (int i = 0; i <array.length; i ++ if (i <array.lenth) ,,,
AnthonyJClink

Câu trả lời:


223

Một cách khác là nối thêm dấu phẩy trước khi bạn nối i, chỉ không ở lần lặp đầu tiên . (Nhân tiện, đừng sử dụng "" + i- bạn không thực sự muốn ghép nối ở đây và StringBuilder có quá tải phụ (int) hoàn toàn tốt.)

int[] array = {1, 2, 3...};
StringBuilder builder = new StringBuilder();

for (int i : array) {
    if (builder.length() != 0) {
        builder.append(",");
    }
    builder.append(i);
}

Điều tuyệt vời ở đây là nó sẽ hoạt động với bất kỳ thứ gì Iterable- bạn không thể luôn lập chỉ mục mọi thứ. ("Thêm dấu phẩy và sau đó xóa nó ở cuối" là một gợi ý hay khi bạn thực sự sử dụng StringBuilder - nhưng nó không hoạt động đối với những thứ như viết vào luồng. )


2
Mẫu tốt, nhưng builder.length ()! = 0 rất dễ vỡ - hãy tưởng tượng một cái gì đó được thêm vào (có điều kiện!) Vào bộ đệm trước vòng lặp của bạn. Thay vào đó, sử dụng một isFirstcờ boolean. Tiền thưởng: nhanh quá.
Jason Cohen

2
@Jason: Tôi chắc chắn sử dụng mẫu "isFirst" trong đó trình xây dựng sẽ không trống trong lần lặp đầu tiên. Tuy nhiên, khi không cần thiết, nó bổ sung một số lượng khá lớn vào việc thực hiện theo kinh nghiệm của tôi.
Jon Skeet

3
@Liverpool (vv): 1000 kiểm tra không cần thiết là rất, rất khó có thể có bất kỳ tác động đáng kể nào đến hiệu suất. Tôi cũng có thể chỉ ra rằng ký tự phụ do giải pháp của Dinah thêm vào có thể khiến StringBuilder phải mở rộng, tăng gấp đôi kích thước của chuỗi cuối cùng với (tiếp)
Jon Skeet

2
không gian đệm không cần thiết. Tuy nhiên, điểm quan trọng là khả năng đọc. Tôi tình cờ tìm thấy phiên bản của mình dễ đọc hơn Dinah. Nếu bạn cảm thấy ngược lại, điều đó tốt. Tôi chỉ xem xét tác động hiệu suất của "ifs" không cần thiết sau khi tìm thấy nút cổ chai.
Jon Skeet

3
Ngoài ra, giải pháp của tôi là một giải pháp thường được áp dụng hơn, vì nó chỉ dựa vào khả năng viết. Bạn có thể lấy giải pháp của tôi và thay đổi nó để ghi thành luồng, v.v. - nơi bạn không thể lấy lại dữ liệu không cần thiết sau đó. Tôi thích các mẫu thường được áp dụng.
Jon Skeet

145

Một cách khác để làm điều này:

String delim = "";
for (int i : ints) {
    sb.append(delim).append(i);
    delim = ",";
}

Cập nhật: Đối với Java 8, bây giờ bạn có Bộ sưu tập


1
Phải xóa câu trả lời tương tự của tôi - điều đó sẽ dạy tôi không đăng bài trước khi xem xét kỹ hơn các câu trả lời khác.
Michael Burr

1
Cũng lưu ý rằng điều này xử lý vấn đề tiềm năng mà Robert Paulson đã đề cập trong một luồng nhận xét khác - kỹ thuật này không phụ thuộc vào StringBuilder trống khi các giá trị mảng được nối thêm.
Michael Burr

1
Mặc dù mã này mượt hơn / gọn gàng / vui nhộn, nhưng nó không rõ ràng hơn phiên bản if. Luôn luôn sử dụng phiên bản rõ ràng hơn / dễ đọc hơn.
Bill K

8
@Bill: Đó không phải là một quy tắc khó và nhanh; có những lúc giải pháp 'thông minh' là giải pháp "chính xác hơn". Hơn nữa, bạn có thực sự thấy phiên bản này khó đọc không? Dù bằng cách nào thì một người bảo trì sẽ cần phải bước qua nó - tôi không nghĩ sự khác biệt là đáng kể.
Trường hợp Greg

Điều này hoạt động mặc dù bạn ghi vào tài nguyên khác như các cuộc gọi ghi của hệ thống tập tin. Ý tưởng tốt.
tu sĩ 20/8/2015

39

Nó có thể dễ dàng hơn để luôn luôn nối. Và sau đó, khi bạn hoàn thành vòng lặp của mình, chỉ cần xóa ký tự cuối cùng. Tấn ít điều kiện theo cách đó quá.

Bạn có thể sử dụng StringBuilderdeleteCharAt(int index)với chỉ số hạnh phúclength() - 1


5
giải pháp hiệu quả nhất cho vấn đề này. +1.
Màu đỏ thật.

9
Có thể đó là tôi, nhưng tôi thực sự không thích việc bạn đang xóa thứ gì đó bạn vừa thêm ...
Fortega

2
Fortega: Tôi không thích kiểm tra mỗi lần một thứ gì đó mà tôi sẽ làm 99% lần. Có vẻ hợp lý hơn (và nó nhanh hơn) khi áp dụng giải pháp của Dinah hoặc sblundy.
Trâu

1
Giải pháp thanh lịch nhất theo ý kiến ​​của tôi. Cảm ơn bạn!
Charles Morin

32

Có thể bạn đang sử dụng công cụ sai cho Công việc.

Đây là thủ công hơn những gì bạn đang làm nhưng nó thanh lịch hơn nếu không phải là một "trường học cũ"

 StringBuffer buffer = new StringBuffer();
 Iterator iter = s.iterator();
 while (iter.hasNext()) {
      buffer.append(iter.next());
      if (iter.hasNext()) {
            buffer.append(delimiter);
      }
 }


14

Một giải pháp khác (có lẽ là hiệu quả nhất)

    int[] array = {1, 2, 3};
    StringBuilder builder = new StringBuilder();

    if (array.length != 0) {
        builder.append(array[0]);
        for (int i = 1; i < array.length; i++ )
        {
            builder.append(",");
            builder.append(array[i]);
        }
    }

Đây là một giải pháp tự nhiên ngoại trừ việc nó phụ thuộc vào mảng không bị trống.
orcmid

5
@orcmid: Nếu mảng trống, thì điều này vẫn cho đầu ra chính xác - một chuỗi trống. Tôi không chắc quan điểm của bạn là gì.
Eddie

7

giữ cho nó đơn giản và sử dụng một tiêu chuẩn cho vòng lặp:

for(int i = 0 ; i < array.length ; i ++ ){
    builder.append(array[i]);
    if( i != array.length - 1 ){
        builder.append(',');
    }
}

hoặc chỉ sử dụng apache commons-lang StringUtils.join ()


6

Các vòng lặp rõ ràng luôn hoạt động tốt hơn các vòng lặp ngầm.

builder.append( "" + array[0] );
for( int i = 1; i != array.length; i += 1 ) {
   builder.append( ", " + array[i] );
}

Bạn nên bao bọc toàn bộ trong một câu lệnh if trong trường hợp bạn đang xử lý một mảng có độ dài bằng không.


Tôi thích cách tiếp cận này vì nó không cần thực hiện kiểm tra mỗi lần lặp. Điều duy nhất tôi muốn thêm là trình xây dựng có thể nối các số nguyên trực tiếp nên không cần sử dụng "" + int, chỉ cần nối thêm (mảng [0]).
Josh

Bạn cần thêm một nếu xung quanh toàn bộ lô để đảm bảo rằng mảng có ít nhất một phần tử.
Tom Leys

2
Tại sao tất cả mọi người tiếp tục sử dụng các ví dụ với nối chuỗi bên trong của phần bổ sung? "," + x biên dịch thành StringBuilder mới (",") .append (x) .toString () ...
John Gardner

@Josh và @John: Chỉ cần làm theo ví dụ n00b. Đừng muốn giới thiệu quá nhiều thứ cùng một lúc. Quan điểm của bạn là rất tốt, tuy nhiên.
S.Lott

@Tom: Phải. Tôi nghĩ rằng tôi đã nói rằng. Không cần chỉnh sửa.
S.Lott

4

Nếu bạn chuyển đổi nó thành một vòng lặp chỉ số cổ điển, có.

Hoặc bạn chỉ có thể xóa dấu phẩy cuối cùng sau khi hoàn thành. Thích như vậy:

int[] array = {1, 2, 3...};
StringBuilder

builder = new StringBuilder();

for(int i : array)
{
    builder.append(i + ",");
}

if(builder.charAt((builder.length() - 1) == ','))
    builder.deleteCharAt(builder.length() - 1);

Tôi, tôi chỉ sử dụng StringUtils.join()từ commons-lang .


3

Bạn cần phân tách lớp .

Separator s = new Separator(", ");
for(int i : array)
{
     builder.append(s).append(i);
}

Việc thực hiện lớp học Separatorlà thẳng về phía trước. Nó bao bọc một chuỗi được trả về trên mỗi cuộc gọi toString()ngoại trừ cuộc gọi đầu tiên, trả về một chuỗi trống.


3

Dựa trên java.util.Ab tríchCollection.toString (), nó thoát sớm để tránh dấu phân cách.

StringBuffer buffer = new StringBuffer();
Iterator iter = s.iterator();
for (;;) {
  buffer.append(iter.next());
  if (! iter.hasNext())
    break;
  buffer.append(delimiter);
}

Đó là hiệu quả và thanh lịch, nhưng không rõ ràng như một số câu trả lời khác.


Bạn đã quên bao gồm sự trở lại sớm khi (! i.hasNext()), đó là một phần quan trọng của sự mạnh mẽ của phương pháp tổng thể. (Các giải pháp khác ở đây xử lý các bộ sưu tập trống một cách duyên dáng, vì vậy bạn cũng nên như vậy! :)
Mike Clark

3

Đây là một giải pháp:

int[] array = {1, 2, 3...};
StringBuilder builder = new StringBuilder();
bool firstiteration=true;

for(int i : array)
{
    if(!firstiteration)
        builder.append(",");

    builder.append("" + i);
    firstiteration=false;
}

Hãy tìm lần lặp đầu tiên :)  


3

Như bộ công cụ đã đề cập, trong Java 8 bây giờ chúng ta có Collector . Đây là mã sẽ như thế nào:

String joined = array.stream().map(Object::toString).collect(Collectors.joining(", "));

Tôi nghĩ đó chính xác là những gì bạn đang tìm kiếm và đó là một mô hình bạn có thể sử dụng cho nhiều thứ khác.


1

Một lựa chọn khác.

StringBuilder builder = new StringBuilder();
for(int i : array)
    builder.append(',').append(i);
String text = builder.toString();
if (text.startsWith(",")) text=text.substring(1);

Tôi đã thực hiện một biến thể ở câu hỏi khác
13ren

1

Nhiều giải pháp được mô tả ở đây hơi vượt trội, IMHO, đặc biệt là những giải pháp dựa vào các thư viện bên ngoài. Có một thành ngữ rõ ràng, sạch đẹp để đạt được một danh sách được phân tách bằng dấu phẩy mà tôi luôn sử dụng. Nó dựa vào toán tử có điều kiện (?):

Chỉnh sửa : Giải pháp ban đầu đúng, nhưng không tối ưu theo ý kiến. Thử lần thứ hai:

    int[] array = {1, 2, 3};
    StringBuilder builder = new StringBuilder();
    for (int i = 0 ;  i < array.length; i++)
           builder.append(i == 0 ? "" : ",").append(array[i]); 

Ở đó bạn đi, trong 4 dòng mã bao gồm khai báo mảng và StringBuilder.


Nhưng bạn đang tạo StringBuilder mới trên mỗi lần lặp (nhờ toán tử +).
Michael Myers

Vâng, nếu bạn nhìn vào mã byte, bạn đã đúng. Tôi không thể tin rằng một câu hỏi đơn giản như vậy có thể trở nên phức tạp như vậy. Tôi tự hỏi nếu trình biên dịch có thể tối ưu hóa ở đây.
Julien Chastang

Cung cấp thứ 2, hy vọng giải pháp tốt hơn.
Julien Chastang

1

Đây là một điểm chuẩn SSCCE tôi đã chạy (liên quan đến những gì tôi phải thực hiện) với các kết quả sau:

elapsed time with checks at every iteration: 12055(ms)
elapsed time with deletion at the end: 11977(ms)

Trên ví dụ của tôi ít nhất, bỏ qua việc kiểm tra tại mỗi lần lặp không phải là nhanh hơn trông thấy nhất là đối với khối lượng lành mạnh của dữ liệu, nhưng nó nhanh hơn.

import java.util.ArrayList;
import java.util.List;


public class TestCommas {

  public static String GetUrlsIn(int aProjectID, List<String> aUrls, boolean aPreferChecks)
  {

    if (aPreferChecks) {

      StringBuffer sql = new StringBuffer("select * from mytable_" + aProjectID + " WHERE hash IN ");

      StringBuffer inHashes = new StringBuffer("(");
      StringBuffer inURLs = new StringBuffer("(");

      if (aUrls.size() > 0)
      {

      for (String url : aUrls)
      {

        if (inHashes.length() > 0) {
        inHashes.append(",");
        inURLs.append(",");
        }

        inHashes.append(url.hashCode());

        inURLs.append("\"").append(url.replace("\"", "\\\"")).append("\"");//.append(",");

      }

      }

      inHashes.append(")");
      inURLs.append(")");

      return sql.append(inHashes).append(" AND url IN ").append(inURLs).toString();
    }

    else {

      StringBuffer sql = new StringBuffer("select * from mytable" + aProjectID + " WHERE hash IN ");

      StringBuffer inHashes = new StringBuffer("(");
      StringBuffer inURLs = new StringBuffer("(");

      if (aUrls.size() > 0)
      {

      for (String url : aUrls)
      {
        inHashes.append(url.hashCode()).append(","); 

        inURLs.append("\"").append(url.replace("\"", "\\\"")).append("\"").append(",");
      }

      }

      inHashes.deleteCharAt(inHashes.length()-1);
      inURLs.deleteCharAt(inURLs.length()-1);

      inHashes.append(")");
      inURLs.append(")");

      return sql.append(inHashes).append(" AND url IN ").append(inURLs).toString();
    }

  }

  public static void main(String[] args) { 
        List<String> urls = new ArrayList<String>();

    for (int i = 0; i < 10000; i++) {
      urls.add("http://www.google.com/" + System.currentTimeMillis());
      urls.add("http://www.yahoo.com/" + System.currentTimeMillis());
      urls.add("http://www.bing.com/" + System.currentTimeMillis());
    }


    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 300; i++) {
      GetUrlsIn(5, urls, true);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("elapsed time with checks at every iteration: " + (endTime-startTime) + "(ms)");

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 300; i++) {
      GetUrlsIn(5, urls, false);
    }
    endTime = System.currentTimeMillis();
    System.out.println("elapsed time with deletion at the end: " + (endTime-startTime) + "(ms)");
  }
}

1
Vui lòng không cuộn điểm chuẩn của riêng bạn và sử dụng khai thác điểm chuẩn vi mô riêng của OpenJDK (jmh) . Nó sẽ làm nóng mã của bạn và tránh những cạm bẫy có vấn đề nhất.
René

0

Một cách tiếp cận khác là để độ dài của mảng (nếu có) được lưu trữ trong một biến riêng biệt (hiệu quả hơn là kiểm tra lại độ dài mỗi lần). Sau đó, bạn có thể so sánh chỉ mục của mình với độ dài đó để xác định có thêm dấu phẩy cuối cùng hay không.

EDIT: Một cân nhắc khác là cân nhắc chi phí hiệu năng của việc loại bỏ một ký tự cuối cùng (có thể gây ra một bản sao chuỗi) chống lại việc có một điều kiện được kiểm tra trong mỗi lần lặp.


Xóa một ký tự cuối cùng không nên tạo ra một bản sao chuỗi. Các phương thức xóa / xóaCharAt của StringBuffer cuối cùng gọi phương thức sau bên trong mảng hệ thống nguồn và đích giống hệt lớp hệ thống: System -> public static void void Arraycopy (Object src, int srcPos, Object Dest, int DestPos, int length);
Trâu

0

Nếu bạn chỉ biến một mảng thành một mảng được phân cách bằng dấu phẩy, nhiều ngôn ngữ có chức năng nối chính xác cho điều này. Nó biến một mảng thành một chuỗi với một dấu phân cách giữa mỗi phần tử.


0

Trong trường hợp này thực sự không cần phải biết nếu đó là sự lặp lại cuối cùng. Có nhiều cách chúng ta có thể giải quyết điều này. Một cách sẽ là:

String del = null;
for(int i : array)
{
    if (del != null)
       builder.append(del);
    else
       del = ",";
    builder.append(i);
}

0

Hai đường dẫn thay thế ở đây:

1: Sử dụng chuỗi Apache Commons

2: Giữ một boolean được gọi first, đặt thành đúng. Trong mỗi lần lặp, nếu firstsai, hãy thêm dấu phẩy của bạn; sau đó, đặt firstthành false.


1) Liên kết đã chết 2) Tôi mất nhiều thời gian hơn để đọc mô tả thay vì mã :)
Buffalo

0

Vì là một mảng cố định, sẽ dễ dàng hơn để tránh tăng cường cho ... Nếu Đối tượng là một bộ sưu tập, một trình vòng lặp sẽ dễ dàng hơn.

int nums[] = getNumbersArray();
StringBuilder builder = new StringBuilder();

// non enhanced version
for(int i = 0; i < nums.length; i++){
   builder.append(nums[i]);
   if(i < nums.length - 1){
       builder.append(",");
   }   
}

//using iterator
Iterator<int> numIter = Arrays.asList(nums).iterator();

while(numIter.hasNext()){
   int num = numIter.next();
   builder.append(num);
   if(numIter.hasNext()){
      builder.append(",");
   }
}
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.