Có một Java tương đương với từ khóa 'sản lượng' của C # không?


112

Tôi biết không có tương đương trực tiếp trong chính Java, nhưng có lẽ là một bên thứ ba?

Nó thực sự thuận tiện. Hiện tại, tôi muốn triển khai một trình lặp mang lại tất cả các nút trong một cây, có khoảng năm dòng mã có năng suất.


6
Tôi biết rồi mà. Nhưng tôi nghĩ rằng biết nhiều ngôn ngữ hơn là sức mạnh hơn. Hơn nữa, việc phát triển phần phụ trợ (mà tôi đang làm) trong công ty tôi đang làm việc hiện đang được thực hiện bằng Java, vì vậy tôi thực sự không thể chọn ngôn ngữ :(
ripper234

Câu trả lời:


91

Hai lựa chọn mà tôi biết là thư viện infomancers-collection của Aviad Ben Dov từ năm 2007thư viện YieldAdapter của Jim Blackler từ năm 2008 (cũng được đề cập trong câu trả lời khác).

Cả hai đều sẽ cho phép bạn viết mã với yield returncấu trúc giống như trong Java, vì vậy cả hai đều sẽ đáp ứng yêu cầu của bạn. Sự khác biệt đáng chú ý giữa hai là:

Cơ học

Thư viện của Aviad đang sử dụng thao tác bytecode trong khi của Jim sử dụng đa luồng. Tùy thuộc vào nhu cầu của bạn, mỗi loại có thể có ưu và nhược điểm riêng. Có thể giải pháp của Aviad nhanh hơn, trong khi Jim's di động hơn (ví dụ: tôi không nghĩ rằng thư viện của Aviad sẽ hoạt động trên Android).

Giao diện

Thư viện của Aviad có giao diện gọn gàng hơn - đây là một ví dụ:

Iterable<Integer> it = new Yielder<Integer>() {
    @Override protected void yieldNextCore() {
        for (int i = 0; i < 10; i++) {
            yieldReturn(i);
            if (i == 5) yieldBreak();
        }
    }
};

Trong khi Jim's phức tạp hơn, đòi hỏi bạn phải có một phương pháp adeptchung chung ... ugh. Tuy nhiên, bạn có thể sử dụng một cái gì đó giống như trình bao bọc này xung quanh mã của Jim bởi Zoom Information giúp đơn giản hóa điều đó một cách đáng kể:Collectorcollect(ResultHandler)

Iterable<Integer> it = new Generator<Integer>() {
    @Override protected void run() {
        for (int i = 0; i < 10; i++) {
            yield(i);
            if (i == 5) return;
        }
    }
};

Giấy phép

Giải pháp của Aviad là BSD.

Giải pháp của Jim là miền công cộng và trình bao bọc của nó cũng được đề cập ở trên.


3
Câu trả lời tuyệt vời. Bạn không chỉ trả lời hoàn toàn câu hỏi mà bạn còn trả lời rất rõ ràng. Ngoài ra, tôi thích định dạng câu trả lời của bạn và cách bạn đưa thông tin giấy phép vào. Tiếp tục câu trả lời tuyệt vời! :)
Malcolm

1
Đừng quên ổi AbstractIterator.
shmosel,

Tôi chỉ xuất bản khác (MIT cấp phép) giải pháp ở đây, mà ra mắt một thread riêng biệt cho các nhà sản xuất, và bộ lập một hàng đợi bị chặn giữa nhà sản xuất và người tiêu dùng: github.com/lukehutch/Producer
Luke Hutchison

Lưu ý rằng điều đó yield(i)sẽ phá vỡ kể từ JDK 13, vì yieldđang được thêm vào dưới dạng một câu lệnh Java / từ khóa dành riêng mới.
Luke Hutchison

14

Cả hai cách tiếp cận này đều có thể trở nên gọn gàng hơn một chút khi Java đã có Lambdas. Bạn có thể làm một cái gì đó như

public Yielderable<Integer> oneToFive() {
    return yield -> {
        for (int i = 1; i < 10; i++) {
            if (i == 6) yield.breaking();
            yield.returning(i);
        }
    };
}

Tôi giải thích thêm một chút ở đây.


4
Yielderable? Nó không nên được Yieldable? (động từ là chỉ 'năng suất', không 'yielder' hoặc 'yielderate' hoặc bất cứ điều gì)
Jochem Kuijpers

yield -> { ... }sẽ phá vỡ kể từ JDK 13, vì yieldđang được thêm vào như một câu lệnh Java mới / từ khóa dành riêng.
Luke Hutchison

1

Tôi biết đó là một câu hỏi rất cũ ở đây và có hai cách được mô tả ở trên:

  • thao tác bytecode không dễ dàng như vậy trong khi chuyển;
  • dựa trên luồng yieldrõ ràng có chi phí tài nguyên.

Tuy nhiên, có một cách khác, thứ ba và có lẽ là cách tự nhiên nhất, để triển khai trình yieldtạo trong Java là cách triển khai gần nhất với những gì trình biên dịch C # 2.0+ làm cho yield return/breaktạo: lombok-pg . Nó hoàn toàn dựa trên một máy trạng thái và cần có sự hợp tác chặt chẽ javacđể thao tác mã nguồn AST. Thật không may, hỗ trợ lombok-pg dường như đã bị ngừng (không có hoạt động kho lưu trữ trong hơn một hoặc hai năm) và Dự án Lombok ban đầu không may thiếu yieldtính năng (mặc dù nó có IDE tốt hơn như Eclipse, hỗ trợ IntelliJ IDEA).


1

Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) không giống như toán tử năng suất, nhưng có thể hữu ích khi viết các trình tạo của riêng bạn theo cách này:

import java.util.stream.Stream;
public class Test01 {
    private static void myFoo(int someVar){
        //do some work
        System.out.println(someVar);
    }
    private static void myFoo2(){
        //do some work
        System.out.println("some work");
    }
    public static void main(String[] args) {
        Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo);     //var1
        Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2());  //var2
    }
}

0

Tôi cũng đề nghị nếu bạn đang sử dụng RXJava trong dự án của mình, hãy sử dụng Observable làm "yielder". Nó có thể được sử dụng theo cách tương tự nếu bạn làm cho nó có thể quan sát được.

public class Example extends Observable<String> {

    public static void main(String[] args) {
        new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
    }

    @Override
    protected void subscribeActual(Observer<? super String> observer) {
        observer.onNext("a"); // yield
        observer.onNext("b"); // yield
        observer.onNext("c"); // yield
        observer.onNext("d"); // yield
        observer.onComplete(); // finish
    }
}

Các tệp quan sát có thể được chuyển đổi thành trình vòng lặp để bạn thậm chí có thể sử dụng chúng trong các vòng lặp for truyền thống hơn. Ngoài ra RXJava cung cấp cho bạn những công cụ thực sự mạnh mẽ, nhưng nếu bạn chỉ cần một cái gì đó đơn giản thì có lẽ đây sẽ là một sự quá mức cần thiết.


0

Tôi vừa xuất bản một giải pháp khác (được MIT cấp phép) tại đây , này khởi chạy nhà sản xuất trong một chuỗi riêng biệt và thiết lập hàng đợi giới hạn giữa nhà sản xuất và người tiêu dùng, cho phép tạo bộ đệm, kiểm soát luồng và kết nối song song giữa nhà sản xuất và người tiêu dùng (vì vậy rằng người tiêu dùng có thể làm việc để tiêu thụ mặt hàng trước trong khi người sản xuất đang làm việc để sản xuất mặt hàng tiếp theo).

Bạn có thể sử dụng biểu mẫu lớp bên trong ẩn danh này:

Iterable<T> iterable = new Producer<T>(queueSize) {
    @Override
    public void producer() {
        produce(someT);
    }
};

ví dụ:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
    @Override
    public void producer() {
        for (int i = 0; i < 20; i++) {
            System.out.println("Producing " + i);
            produce(i);
        }
        System.out.println("Producer exiting");
    }
}) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}

Hoặc bạn có thể sử dụng ký hiệu lambda để cắt bớt trên bảng điện tử:

for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
    for (int i = 0; i < 20; i++) {
        System.out.println("Producing " + i);
        producer.produce(i);
    }
    System.out.println("Producer exiting");
})) {
    System.out.println("  Consuming " + item);
    Thread.sleep(200);
}
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.