Chuỗi CharSequence VS trong Java?


421

Lập trình trong Android, hầu hết các giá trị văn bản được mong đợi CharSequence.

Tại sao vậy? Lợi ích là gì và tác động chính của việc sử dụng CharSequencehơn là Stringgì?

Sự khác biệt chính là gì, và những vấn đề được mong đợi, trong khi sử dụng chúng, và chuyển đổi từ cái này sang cái khác?


1
Câu trả lời tốt hơn có thể được tìm thấy ở sự khác biệt chính xác giữa CharSequence và String trong java
Suragch

Câu trả lời:


343

Chuỗi là CharSequences , vì vậy bạn chỉ có thể sử dụng Chuỗi và không phải lo lắng. Android chỉ đang cố gắng trở nên hữu ích bằng cách cho phép bạn cũng chỉ định các đối tượng CharSequence khác, như StringBuffers.


94
Ngoại trừ khi Android chuyển cho tôi một CharSequence trong một cuộc gọi lại và tôi cần một Chuỗi - hãy gọi charSeq.toString ().
Martin Konicek

100
Nhưng hãy nhớ rằng cảnh báo này từ CharSequencejavadoc: Giao diện này không tinh chỉnh các hợp đồng chung của phương thức equalshashCode. Do đó, kết quả của việc so sánh hai đối tượng thực hiện CharSequencekhông xác định . Mỗi đối tượng có thể được thực hiện bởi một lớp khác nhau và không có gì đảm bảo rằng mỗi lớp sẽ có khả năng kiểm tra các thể hiện của nó để tìm sự bình đẳng với các lớp khác. Do đó, không phù hợp để sử dụng các CharSequencethể hiện tùy ý làm các thành phần trong một tập hợp hoặc làm các khóa trong bản đồ.
Trevor Robinson

3
@Pacerier: Tôi nghĩ đó là một hạn chế thực tế. CharSequencelà một trang bị thêm trong JDK 1.4 để giới thiệu một giao diện chung có mục đích giới hạn cho các đối tượng chứa các chuỗi ký tự. Một số đối tượng chứa trạng thái khác, vì vậy có thể không có nghĩa khi định nghĩa Object.equalslà "chứa cùng một chuỗi ký tự". NIO CharBuffer, ví dụ, chỉ hiển thị các ký tự giữa nó positionlimitnhư CharSequence, mặc dù có khả năng giữ nhiều nhân vật khác.
Trevor Robinson

6
@TrevorRobinson, Vì vậy, lỗi thiết kế đang có equals/ hashCodebật Objectngay từ đầu ....
Pacerier

4
@Pacerier: IMHO Không có lỗi thiết kế nào trong Objecthoặc CharSequence, không có giao diện nào được yêu cầu để cung cấp sự bình đẳng giữa các lần triển khai. Không có hai Collections được yêu cầu để cung cấp sự bình đẳng giữa các Collectiongiao diện, nhưng họ có thể nếu họ chọn. IMHO CharSequencenên được giới hạn ở đầu vào và được sử dụng ít hơn cho các loại trả lại.
Brett Ryan

58

CharSequence= giao diện
String= thực hiện cụ thể

Bạn đã nói:

chuyển đổi từ cái này sang cái khác

Không có chuyển đổi từ String .

  • Mọi Stringđối tượng một CharSequence.
  • Mỗi CharSequencecó thể sản xuất a String. Gọi CharSequence::toString. Nếu CharSequencexảy ra là a String, thì phương thức trả về một tham chiếu đến đối tượng của chính nó.

Nói cách khác, mỗi Stringlà một CharSequence, nhưng không phải mọi CharSequencelà một String.

Lập trình đến một giao diện

Lập trình trong Android, hầu hết các giá trị văn bản được mong đợi trong CharSequence.

Tại sao vậy? Lợi ích là gì và tác động chính của việc sử dụng CharSequence trên String là gì?

Nói chung, lập trình cho một giao diện tốt hơn so với lập trình cho các lớp cụ thể. Điều này mang lại sự linh hoạt, vì vậy chúng ta có thể chuyển đổi giữa các triển khai cụ thể của một giao diện cụ thể mà không vi phạm mã khác.

Khi phát triển API được sử dụng bởi các lập trình viên khác nhau trong các tình huống khác nhau, hãy viết mã của bạn để đưa ra và lấy các giao diện chung nhất có thể. Điều này cho phép lập trình viên gọi tự do sử dụng các triển khai khác nhau của giao diện đó, bất kỳ triển khai nào là tốt nhất cho bối cảnh cụ thể của họ.

Ví dụ, nhìn vào Khung sưu tập Java . Nếu API của bạn cung cấp cho hoặc mất một bộ sưu tập lệnh của các đối tượng, kê khai các phương pháp của bạn như sử dụng Listchứ không phải là ArrayList, LinkedListhoặc bất kỳ thực hiện của bên thứ 3 khác List.

Khi viết một phương thức nhỏ nhanh và bẩn chỉ được sử dụng bởi mã của bạn ở một nơi cụ thể, trái ngược với việc viết API được sử dụng ở nhiều nơi, bạn không cần phải sử dụng giao diện chung chung hơn là cụ thể lớp học. Nhưng ngay cả khi đó, thật đau lòng khi sử dụng giao diện chung nhất mà bạn có thể.

Sự khác biệt chính là gì và những vấn đề được mong đợi, trong khi sử dụng chúng,

  • Với một Stringbạn biết bạn có một đoạn văn bản duy nhất, hoàn toàn trong bộ nhớ và là bất biến.
  • Với một CharSequence, bạn không biết các tính năng cụ thể của việc triển khai cụ thể có thể là gì.

Đối CharSequencetượng có thể đại diện cho một đoạn văn bản khổng lồ, và do đó có ý nghĩa bộ nhớ. Hoặc có thể nhiều đoạn văn bản được theo dõi riêng sẽ cần được ghép lại với nhau khi bạn gọi toString, và do đó có vấn đề về hiệu suất. Việc triển khai thậm chí có thể lấy văn bản từ một dịch vụ từ xa và do đó có ý nghĩa về độ trễ.

và chuyển đổi từ cái này sang cái khác?

Bạn thường sẽ không được chuyển đổi qua lại. A String một CharSequence. Nếu phương thức của bạn tuyên bố rằng nó cần một CharSequence, lập trình viên gọi có thể truyền một Stringđối tượng hoặc có thể truyền một cái gì đó khác như a StringBufferhoặc StringBuilder. Mã phương thức của bạn sẽ đơn giản sử dụng bất cứ thứ gì được thông qua, gọi bất kỳ CharSequencephương thức nào.

Cách gần nhất bạn có thể chuyển đổi là nếu mã của bạn nhận được a CharSequencevà bạn biết bạn cần a String. Có lẽ bạn đang giao tiếp với mã cũ được viết cho Stringlớp chứ không phải được ghi vào CharSequencegiao diện. Hoặc có lẽ mã của bạn sẽ làm việc mạnh mẽ với văn bản, chẳng hạn như lặp đi lặp lại hoặc phân tích. Trong trường hợp đó, bạn muốn thực hiện bất kỳ hiệu suất có thể đạt được chỉ một lần, vì vậy bạn gọitoString lên trước. Sau đó tiến hành công việc của bạn bằng cách sử dụng những gì bạn biết là một đoạn văn bản hoàn toàn trong bộ nhớ.

Lịch sử xoắn

Lưu ý các ý kiến ​​được thực hiện trên câu trả lời được chấp nhận . Các CharSequencegiao diện đã được trang bị thêm vào cấu trúc lớp học hiện có, vì vậy có một số tinh tế quan trọng ( equals()& hashCode()). Lưu ý các phiên bản khác nhau của Java (1, 2, 4 & 5) được gắn thẻ trên các lớp / giao diện. Lý tưởng nhất CharSequenceđã có được ngay từ đầu, nhưng đó là cuộc sống.

Sơ đồ lớp của tôi dưới đây có thể giúp bạn thấy bức tranh lớn về các loại chuỗi trong Java 7/8. Tôi không chắc liệu tất cả những thứ này có trong Android hay không, nhưng bối cảnh chung vẫn có thể hữu ích với bạn.

sơ đồ của các lớp và giao diện liên quan đến chuỗi


2
Bạn đã thực hiện sơ đồ này cho mình? tự hỏi nếu có một danh mục của các sơ đồ này cho các cấu trúc dữ liệu khác nhau.
dùng171943

4
@ user171943 Được ủy quyền bởi tôi, được tạo thủ công bằng ứng dụng OmniGraffle từ Omnigroup.
Basil Bourque

37

Tôi tin rằng tốt nhất là sử dụng CharSequence. Lý do là String thực hiện CharSequence, do đó bạn có thể chuyển Chuỗi thành CharSequence, TUY NHIÊN bạn không thể chuyển CharSequence thành Chuỗi, vì CharSequence không triển khai Chuỗi. CSONG, trong Android EditText.getText()phương thức trả về một Editable, cũng thực hiện CharSequence và có thể dễ dàng chuyển thành một, trong khi không dễ dàng thành Chuỗi. CharSequence xử lý tất cả!


7
Bạn có thể làmcharSequence.toString()
Jorge Fuentes González

1
@jorge: Ngoại trừ việc đó sẽ tương đối kém hiệu quả nếu chuỗi có thể thay đổi (hoặc vì bất kỳ lý do nào, yêu cầu một bản sao của các ký tự để tạo thành chuỗi bất biến).
Lawrence Dol

lời giải thích rất hay ..!
majurageerthan

23

Nói chung, sử dụng một giao diện cho phép bạn thay đổi việc thực hiện với thiệt hại tài sản thế chấp tối thiểu. Mặc dù java.lang.String là siêu phổ biến nhưng có thể trong một số bối cảnh nhất định, người ta có thể muốn sử dụng một triển khai khác. Bằng cách xây dựng API xung quanh CharSequences thay vì String, mã sẽ cho một cơ hội để làm điều đó.


8

Đây gần như chắc chắn là lý do hiệu suất. Ví dụ: hãy tưởng tượng một trình phân tích cú pháp đi qua một chuỗi ByteBuffer 500k có chứa các chuỗi.

Có 3 cách tiếp cận để trả về nội dung chuỗi:

  1. Xây dựng một chuỗi [] tại thời điểm phân tích, một ký tự tại một thời điểm. Điều này sẽ mất một lượng thời gian đáng chú ý. Chúng ta có thể sử dụng == thay vì .equals để so sánh các tham chiếu được lưu trong bộ nhớ cache.

  2. Xây dựng một int [] với độ lệch tại thời điểm phân tích cú pháp, sau đó tự động xây dựng Chuỗi khi xảy ra get (). Mỗi Chuỗi sẽ là một đối tượng mới, do đó, không có giá trị trả về bộ đệm và sử dụng ==

  3. Xây dựng một CharSequence [] tại thời điểm phân tích cú pháp. Vì không có dữ liệu mới nào được lưu trữ (trừ offset vào bộ đệm byte), nên việc phân tích cú pháp thấp hơn nhiều so với # 1. Khi có thời gian, chúng ta không cần phải xây dựng Chuỗi, do đó, hiệu suất có được bằng # 1 (tốt hơn nhiều so với # 2), vì chúng ta chỉ trả lại một tham chiếu đến một đối tượng hiện có.

Ngoài các lợi ích xử lý bạn có được khi sử dụng CharSequence, bạn cũng giảm dung lượng bộ nhớ bằng cách không sao chép dữ liệu. Ví dụ: nếu bạn có bộ đệm chứa 3 đoạn văn bản và muốn trả về cả 3 hoặc một đoạn văn, bạn cần 4 Chuỗi để thể hiện điều này. Sử dụng CharSequence, bạn chỉ cần 1 bộ đệm với dữ liệu và 4 trường hợp triển khai CharSequence theo dõi thời gian bắt đầu và thời lượng.


6
tài liệu tham khảo plz. Nghe có vẻ như đoán ngẫu nhiên những gì đang xảy ra. Ngoài ra tôi không tìm thấy đối số của bạn hợp lệ. người ta có thể chỉ cần lưu trữ 500k bytebuffer dưới dạng một chuỗi ở vị trí đầu tiên và chỉ trả về các chuỗi con, điều này thật nhanh chóng và phổ biến hơn nhiều.
kritzikratzi

6
@kritzikratzi - kể từ JDK7, chuỗi con trên Chuỗi không còn chia sẻ mảng cơ bản và không "điên nhanh". Phải mất thời gian O (N) theo chiều dài của chuỗi con và tạo một bản sao của các ký tự bên dưới mỗi lần bạn gọi nó (rất nhiều rác).
BeeOnRope

@kritzikratzi Tôi tin rằng lý do cho sự thay đổi là vì nếu bản sao không được tạo, Chuỗi gốc sẽ được giữ lại trong suốt vòng đời của tất cả các chuỗi con. Cho rằng các chuỗi con thường chỉ là các phần nhỏ của bản gốc và có thể tồn tại vô thời hạn tùy thuộc vào cách chúng được sử dụng, điều này thường sẽ dẫn đến nhiều rác hơn nếu các chuỗi được sử dụng lâu hơn nhiều so với chuỗi ban đầu. Một alt thú vị có thể là để xác định xem có sao chép hay không dựa trên tỷ lệ của chuỗi con so với kích thước chuỗi cha, nhưng bạn phải tự CharSequencethực hiện việc đó.
JAB

1
Có lẽ tôi đã bỏ lỡ vấn đề, nhưng Câu trả lời này là vô nghĩa. A CharSequencelà một giao diện - theo định nghĩa, nó không có chi tiết triển khai nào mà bạn thảo luận vì nó không có triển khai riêng. A Stringlà một trong một số lớp cụ thể thực hiện CharSequencegiao diện. Vậy a String a CharSequence. Bạn có thể so sánh chi tiết hiệu suất của Stringvs StringBuffervs StringBuilder, nhưng không CharSequence. Viết xử lý lợi ích mà bạn nhận được bằng cách sử dụng CharSequence, là vô nghĩa.
Basil Bourque

7

Một vấn đề mà DO phát sinh trong mã Android thực tế là việc so sánh chúng với CharSequence.equals là hợp lệ nhưng không nhất thiết phải hoạt động như dự định.

EditText t = (EditText )getView(R.id.myEditText); // Contains "OK"
Boolean isFalse = t.getText().equals("OK"); // will always return false.

So sánh nên được thực hiện bởi

("OK").contentEquals(t.GetText()); 

5

Trình tự Char

A CharSequencelà một giao diện, không phải là một lớp thực tế. Một giao diện chỉ là một tập hợp các quy tắc (phương thức) mà một lớp phải chứa nếu nó thực hiện giao diện. Trong Android, một CharSequencechiếc ô cho nhiều loại chuỗi văn bản. Dưới đây là một số trong những phổ biến:

  • String (văn bản bất biến không có nhịp tạo kiểu)
  • StringBuilder (văn bản có thể thay đổi không có nhịp tạo kiểu)
  • SpannableString (văn bản bất biến với các nhịp kiểu dáng)
  • SpannableStringBuilder (văn bản có thể thay đổi với các kiểu dáng)

(Bạn có thể đọc thêm về sự khác biệt giữa những điều này ở đây .)

Nếu bạn có một CharSequenceđối tượng, thì nó thực sự là một đối tượng của một trong các lớp thực hiện CharSequence. Ví dụ:

CharSequence myString = "hello";
CharSequence mySpannableStringBuilder = new SpannableStringBuilder();

Lợi ích của việc có một loại ô chung như thế CharSequencelà bạn có thể xử lý nhiều loại với một phương thức duy nhất. Ví dụ, nếu tôi có một phương thức lấy CharSequencetham số làm tham số, tôi có thể truyền vào một Stringhoặc một SpannableStringBuildervà nó sẽ xử lý một trong hai.

public int getLength(CharSequence text) {
    return text.length();
}

Chuỗi

Bạn có thể nói rằng a Stringchỉ là một loại CharSequence. Tuy nhiên, không giống như CharSequence, nó là một lớp thực tế, vì vậy bạn có thể tạo các đối tượng từ nó. Vì vậy, bạn có thể làm điều này:

String myString = new String();

nhưng bạn không thể làm điều này:

CharSequence myCharSequence = new CharSequence(); // error: 'CharSequence is abstract; cannot be instantiated

CharSequencechỉ là một danh sách các quy tắc Stringphù hợp với, bạn có thể làm điều này:

CharSequence myString = new String();

Điều đó có nghĩa là bất cứ khi nào một phương thức yêu cầu a CharSequence, bạn vẫn có thể cung cấp cho nó a String.

String myString = "hello";
getLength(myString); // OK

// ...

public int getLength(CharSequence text) {
    return text.length();
}

Tuy nhiên, điều ngược lại là không đúng sự thật. Nếu phương thức lấy Stringtham số, bạn không thể truyền cho nó một cái gì đó thường chỉ được gọi là a CharSequence, bởi vì nó thực sự có thể là một SpannableStringhoặc một loại khác CharSequence.

CharSequence myString = "hello";
getLength(myString); // error

// ...

public int getLength(String text) {
    return text.length();
}

0

CharSequencelà một giao diện và Stringthực hiện nó. Bạn có thể khởi tạo một Stringnhưng bạn không thể làm điều đó CharSequencevì nó là một giao diện. Bạn có thể tìm thấy các triển khai khác trong CharSequencetrang web Java chính thức.


-4

CharSequence là một chuỗi các giá trị char có thể đọc được, thực hiện Chuỗi. nó có 4 phương pháp

  1. charAt (chỉ số int)
  2. chiều dài()
  3. subSequence (int start, int end)
  4. toString ()

Vui lòng tham khảo tài liệu Tài liệu CharSequence


6
CharSequencekhông thực hiện String. Điều ngược lại là đúng, mặc dù.
seh

1
Vui lòng xóa câu trả lời vô nghĩa này
J. Doe
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.