Ý nghĩa của dấu ngã kép (~ ~) trong Java là gì?


192

Khi duyệt mã nguồn của Guava, tôi đã bắt gặp đoạn mã sau (một phần của việc triển khai hashCodecho lớp bên trong CartesianSet):

int adjust = size() - 1;
for (int i = 0; i < axes.size(); i++) {
    adjust *= 31;
    adjust = ~~adjust;
    // in GWT, we have to deal with integer overflow carefully
}
int hash = 1;
for (Set<E> axis : axes) {
    hash = 31 * hash + (size() / axis.size() * axis.hashCode());

    hash = ~~hash;
}
hash += adjust;
return ~~hash;

Cả hai adjusthashints. Từ những gì tôi biết về Java, ~có nghĩa là phủ định bitwise, vì vậy adjust = ~~adjusthash = ~~hashnên để các biến không thay đổi. Chạy thử nghiệm nhỏ (dĩ nhiên đã bật xác nhận),

for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
    assert i == ~~i;
}

xác nhận điều này. Giả sử rằng những người ổi biết họ đang làm gì, phải có lý do để họ làm điều này. Câu hỏi là gì?

EDIT Như đã chỉ ra trong các ý kiến, bài kiểm tra ở trên không bao gồm trường hợp ibằng Integer.MAX_VALUE. Vì i <= Integer.MAX_VALUEluôn luôn đúng, chúng ta sẽ cần kiểm tra trường hợp đó bên ngoài vòng lặp để ngăn nó lặp lại mãi mãi. Tuy nhiên, dòng

assert Integer.MAX_VALUE == ~~Integer.MAX_VALUE;

đưa ra cảnh báo trình biên dịch "So sánh các biểu thức giống hệt nhau", điều này khá giống với nó.


42
@dr_andonuts Guava là một thư viện khá chuẩn để đưa vào một dự án những ngày này - Tôi nghĩ rằng lời khuyên để chạy xa là không đúng chỗ.
yshavit

4
Khẳng định không kiểm tra trường hợp cạnh Integer.MAX_VALUE. Tương phản với -(-Integer.MIN_VALUE) != Integer.MIN_VALUE.
Franky

3
@Franky Những gì maaartinus nói. -Integer.MIN_VALUEkết thúc tốt đẹp Integer.MIN_VALUE, để phủ nhận rằng một lần nữa chỉ đơn giản là sản xuất Integer.MIN_VALUElại.

2
@maaartinus, @hvd, cảm ơn vì đã chỉ ra điều đó. Bây giờ tôi nhớ điều đó -x = (~x) + 1.
Franky

7
@dr_andonuts Trolling? Tại sao chạy trốn khỏi những điều bạn không hiểu. Đây là những gì StackOverflow có ở đây: để giúp bạn tìm hiểu.
Jared Burrows

Câu trả lời:


245

Trong Java, nó không có nghĩa gì.

Nhưng nhận xét đó nói rằng dòng này dành riêng cho GWT, đây là một cách để biên dịch Java thành JavaScript.

Trong JavaScript, các số nguyên giống như các số nguyên nhân đôi. Chúng có giá trị tối đa là 2 ^ 53 chẳng hạn. Nhưng các toán tử bitwise xử lý các số như thể chúng là 32 bit, đó chính xác là những gì bạn muốn trong mã này. Nói cách khác, ~~hashnói "coi hashnhư một số 32 bit" trong JavaScript. Cụ thể, nó loại bỏ tất cả trừ 32 bit dưới cùng (vì các toán ~tử bitwise chỉ nhìn vào 32 bit dưới cùng), giống hệt như cách hoạt động tràn của Java.

Nếu bạn không có điều đó, mã băm của đối tượng sẽ khác nhau tùy thuộc vào việc nó được đánh giá trong vùng đất Java hay trong vùng đất JavaScript (thông qua quá trình biên dịch GWT).


10
@harold Đây không phải là một thứ của GWT, nó là một thứ JavaScript. Đó chỉ là con số hoạt động trong ngôn ngữ đó.
yshavit

18
@yshavit Tuy nhiên, đó không phải là cách các con số hoạt động trong Java. Nếu GWT không che giấu thực tế là các số được triển khai khác nhau trong JS và trên JVM từ người dùng, thì đó thực sự là một trình biên dịch kém.
valderman

8
@harold, yeah, đó là JavaScript thực hiện các số nguyên không chính xác (thực tế không có loại số nguyên nào trong JavaScript).
MikeTheLiar

8
@valderman Đó là một điểm tốt. Thêm |0hoặc ~~âm thanh như thế sẽ không khó, mặc dù tôi không biết hiệu suất sẽ là gì (bạn phải thêm nó ở mỗi bước của mỗi biểu thức). Tôi không biết những cân nhắc thiết kế là gì. Fwiw, sự không nhất quán được ghi lại trên trang tương thích của GWT .
yshavit

6
hashCodelà lạ ở chỗ nó cố tình tòa án, hoặc thậm chí mong đợi, tràn ra xảy ra. Nơi duy nhất bạn có thể quan sát sự không nhất quán là nơi một int Java bình thường sẽ tràn qua, đây không phải là vấn đề xuất hiện trong hầu hết các mã; nó chỉ liên quan trong trường hợp kỳ lạ này.
Louis Wasserman
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.