Heim  >  Artikel  >  Java  >  So verwenden Sie die Unicode-Agentenprogrammierung in Java

So verwenden Sie die Unicode-Agentenprogrammierung in Java

PHPz
PHPznach vorne
2023-05-06 20:43:18735Durchsuche

Sequentieller Zugriff

Sequentieller Zugriff ist eine grundlegende Operation zum Verarbeiten von Zeichenfolgen in der Java-Sprache. Bei diesem Ansatz wird auf jedes Zeichen in der Eingabezeichenfolge nacheinander vom Anfang bis zum Ende oder manchmal vom Ende zum Anfang zugegriffen. In diesem Abschnitt werden sieben technische Beispiele für die Erstellung eines 32-Bit-Codepunkt-Arrays aus einer Zeichenfolge mithilfe sequenzieller Zugriffsmethoden erläutert und deren Verarbeitungszeit geschätzt.

Beispiel 1-1: Benchmark (keine Unterstützung für Ersatzpaare)

Listing 1 Einen 16-Bit-Char-Typ-Wert direkt einem 32-Bit-Codepunktwert zuweisen, ohne überhaupt Ersatzpaare zu berücksichtigen:

Listing 1 . Nicht unterstützte Proxy-Paare

int[] toCodePointArray(String str) { // Example 1-1      int len = str.length();          // the length of str      int[] acp = new int[len];        // an array of code points       for (int i = 0, j = 0; i <p> Obwohl dieses Beispiel keine Ersatzpaare unterstützt, bietet es eine Basislinie für die Verarbeitungszeit zum Vergleich mit nachfolgenden Beispielen für sequenziellen Zugriff. </p><p><strong>Beispiel 1-2: Verwendung von isSurrogatePair()</strong></p><p>Listing 2 Verwenden Sie isSurrogatePair(), um die Gesamtzahl der Ersatzpaare zu zählen. Nach dem Zählen wird genügend Speicher für ein Array von Codebits zum Speichern des Werts reserviert. Anschließend tritt es in eine sequentielle Zugriffsschleife ein und verwendet isHighSurrogate() und isLowSurrogate(), um zu bestimmen, ob es sich bei jedem Ersatzzeichenpaar um ein hohes oder niedriges Ersatzzeichen handelt. Wenn ein hoher Ersatzwert gefolgt von einem niedrigen Ersatzwert gefunden wird, verwendet er toCodePoint(), um das Ersatzwertpaar in einen Codepunktwert umzuwandeln, und erhöht den aktuellen Indexwert um 2. Andernfalls wird der Wert vom Typ „char“ direkt einem Codepunktwert zugewiesen und der aktuelle Indexwert um 1 erhöht. Die Verarbeitung dieses Beispiels dauert 1,38-mal länger als Beispiel 1-1. </p><p><strong>Listing 2. Eingeschränkter Support </strong></p><pre class="brush:php;toolbar:false">int[] toCodePointArray(String str) { // Example 1-2      int len = str.length();          // the length of str      int[] acp;                       // an array of code points      int surrogatePairCount = 0;      // the count of surrogate pairs       for (int i = 1; i <p>Der Ansatz zur Aktualisierung von Software in Listing 2 ist naiv. Es ist umständlich und erfordert umfangreiche Modifikationen, wodurch die resultierende Software spröde wird und sich in Zukunft nur schwer ändern lässt. Im Einzelnen sind diese Probleme: </p><p>◆Die Anzahl der Codepunkte muss berechnet werden, um genügend Speicher zuzuweisen </p><p>◆Es ist schwierig, den richtigen Codepunktwert für einen bestimmten Index in der Zeichenfolge zu erhalten </p><p>◆Es ist schwierig, ihn zu verschieben korrekt für den nächsten Verarbeitungsschritt Aktueller Index </p><p> Ein verbesserter Algorithmus erscheint im nächsten Beispiel. </p><p><strong>Beispiel: Grundlegende Unterstützung</strong></p><p>Java 1.5 bietet die Methoden codePointCount(), codePointAt() und offsetByCodePoints(), um die drei Probleme von Beispiel 1-2 zu lösen. Listing 3 verwendet diese Methoden, um die Lesbarkeit dieses Algorithmus zu verbessern: </p><p><strong>Listing 3. Grundlegende Unterstützung </strong></p><pre class="brush:php;toolbar:false">int[] toCodePointArray(String str) { // Example 1-3      int len = str.length();          // the length of str      int[] acp = new int[str.codePointCount(0, len)];       for (int i = 0, j = 0; i <p>Die Verarbeitung von Listing 3 dauert jedoch 2,8-mal länger als Listing 1. </p><p><strong>Beispiel 1-4: Verwendung von codePointBefore()</strong></p><p>Wenn offsetByCodePoints() eine negative Zahl als zweiten Parameter erhält, kann es einen absoluten Offsetwert vom Anfang der Zeichenfolge berechnen. Als nächstes kann codePointBefore() den Codepunktwert vor einem angegebenen Index zurückgeben. Diese Methoden werden verwendet, um die Zeichenfolge vom Ende zum Anfang in Listing 4 zu durchlaufen: </p><p><strong>Listing 4. Verwendung der Basisunterstützung für codePointBefore() </strong></p><pre class="brush:php;toolbar:false">int[] toCodePointArray(String str) { // Example 1-4      int len = str.length();          // the length of str      int[] acp = new int[str.codePointCount(0, len)];      int j = acp.length;              // an index for acp       for (int i = len; i > 0; i = str.offsetByCodePoints(i, -1)) {          acp[--j] = str.codePointBefore(i);      }      return acp;  }

Die Verarbeitung dieses Beispiels dauert 2,72-mal länger als Beispiel 1-1 – Beispiel 1 -3 ist schneller. Im Allgemeinen ist die Codegröße in der JVM kleiner, wenn Sie Null- statt Nicht-Null-Werte vergleichen, was manchmal die Leistung verbessert. Die kleine Verbesserung ist es jedoch möglicherweise nicht wert, die Lesbarkeit zu beeinträchtigen.

Beispiel 1-5: Verwendung von charCount()

Beispiele 1-3 und 1-4 bieten grundlegende Unterstützung für Ersatzpaare. Sie erfordern keine temporären Variablen und sind robuste Codierungsmethoden. Um eine kürzere Verarbeitungszeit zu erzielen, ist die Verwendung von charCount() anstelle von offsetByCodePoints() effektiv, erfordert jedoch eine temporäre Variable zum Speichern des Codepunktwerts, wie in Listing 5 gezeigt:

Listing 5. Optimierung mit charCount() Unterstützung für

int[] toCodePointArray(String str) { // Example 1-5      int len = str.length();          // the length of str      int[] acp = new int[str.codePointCount(0, len)];      int j = 0;                       // an index for acp       for (int i = 0, cp; i <p>Die Verarbeitungszeit von Listing 5 ist auf das 1,68-fache länger als in Beispiel 1-1. </p><p><strong>Beispiel 1-6: Zugriff auf ein char-Array</strong></p><p>Listing 6 Direkter Zugriff auf ein Array vom Typ char unter Verwendung der in Beispiel 1-5 gezeigten Optimierung: </p><p><strong>Listing 6. Optimierte Unterstützung für die Verwendung eines char-Arrays</strong> </p> <pre class="brush:php;toolbar:false">int[] toCodePointArray(String str) { // Example 1-6      char[] ach = str.toCharArray();  // a char array copied from str      int len = ach.length;            // the length of ach      int[] acp = new int[Character.codePointCount(ach, 0, len)];      int j = 0;                       // an index for acp       for (int i = 0, cp; i <p>Char-Arrays werden mit toCharArray() aus Strings kopiert. Die Leistung wird verbessert, da der direkte Zugriff auf das Array schneller ist als der indirekte Zugriff über eine Methode. Die Verarbeitungszeit ist 1,51-mal länger als in Beispiel 1-1. Beim Aufruf erfordert toCharArray() jedoch einen gewissen Aufwand, um ein neues Array zu erstellen und die Daten in das Array zu kopieren. Die von der String-Klasse bereitgestellten Komfortmethoden können ebenfalls nicht verwendet werden. Dieser Algorithmus ist jedoch nützlich, wenn große Datenmengen verarbeitet werden. </p><p><strong>Beispiel 1-7: Ein objektorientierter Algorithmus </strong></p><p>Der objektorientierte Algorithmus dieses Beispiels verwendet die CharBuffer-Klasse, wie in Listing 7 gezeigt: </p><p><strong>Listing 7. Objektorientierter Algorithmus mit CharSequence </strong></p><pre class="brush:php;toolbar:false">int[] toCodePointArray(String str) {        // Example 1-7      CharBuffer cBuf = CharBuffer.wrap(str); // Buffer to wrap str      IntBuffer iBuf = IntBuffer.allocate(    // Buffer to store code points              Character.codePointCount(cBuf, 0, cBuf.capacity()));       while (cBuf.remaining() > 0) {          int cp = Character.codePointAt(cBuf, 0); // the current code point          iBuf.put(cp);          cBuf.position(cBuf.position() + Character.charCount(cp));      }      return iBuf.array();  }

Dasselbe wie zuvor Im Gegensatz zum Beispiel ist in Listing 7 kein Index erforderlich, um die aktuelle Position für den sequentiellen Zugriff zu speichern. Stattdessen verfolgt CharBuffer intern die aktuelle Position. Die Character-Klasse stellt die statischen Methoden codePointCount() und codePointAt() bereit, die CharBuffers über die CharSequence-Schnittstelle verarbeiten. CharBuffer setzt die aktuelle Position immer auf den Kopf der CharSequence. Daher wird beim Aufruf von codePointAt() der zweite Parameter immer auf 0 gesetzt. Die Verarbeitungszeit ist 2,15-mal länger als in Beispiel 1-1.

处理时间比较

这些顺序访问示例的计时测试使用了一个包含 10,000 个代理对和 10,000 个非代理对的样例字符串。码位数组从这个字符串创建 10,000 次。测试环境包括:

◆OS:Microsoft Windows® XP Professional SP2

◆Java:IBM Java 1.5 SR7

◆CPU:Intel® Core 2 Duo CPU T8300 @ 2.40GHz

◆Memory:2.97GB RAM

表 1 展示了示例 1-1 到 1-7 的绝对和相对处理时间以及关联的 API:

表 1. 顺序访问示例的处理时间和 API

So verwenden Sie die Unicode-Agentenprogrammierung in Java

随机访问

随机访问是直接访问一个字符串中的任意位置。当字符串被访问时,索引值基于 16 位 char 类型的单位。但是,如果一个字符串使用 32 位码位,那么它不能使用一个基于 32 位码位的单位的索引访问。必须使用 offsetByCodePoints() 来将码位的索引转换为 char 类型的索引。如果算法设计很糟糕,这会导致很差的性能,因为 offsetByCodePoints() 总是通过使用第二个参数从第一个参数计算字符串的内部。在这个小节中,我将比较三个示例,它们通过使用一个短单位来分割一个长字符串。

示例 2-1:基准测试(不支持代理对)

清单 8 展示如何使用一个宽度单位来分割一个字符串。这个基准测试留作后用,不支持代理对。

清单 8. 不支持代理对

String[] sliceString(String str, int width) { // Example 2-1      // It must be that "str != null && width > 0".      List<string> slices = new ArrayList<string>();      int len = str.length();       // (1) the length of str      int sliceLimit = len - width; // (2) Do not slice beyond here.      int pos = 0;                  // the current position per char type       while (pos <p>sliceLimit 变量对分割位置有所限制,以避免在剩余的字符串不足以分割当前宽度单位时抛出一个 IndexOutOfBoundsException 实例。这种算法在当前位置超出 sliceLimit 时从 while 循环中跳出后再处理最后的分割。</p>
<p><strong>示例 2-2:使用一个码位索引</strong></p>
<p>清单 9 展示了如何使用一个码位索引来随机访问一个字符串:</p>
<p><strong>清单 9. 糟糕的性能</strong></p>
<pre class="brush:php;toolbar:false">String[] sliceString(String str, int width) { // Example 2-2      // It must be that "str != null && width > 0".      List<string> slices = new ArrayList<string>();      int len = str.codePointCount(0, str.length()); // (1) code point count [Modified]      int sliceLimit = len - width; // (2) Do not slice beyond here.      int pos = 0;                  // the current position per code point       while (pos <p>清单 9 修改了 清单 8 中的几行。首先,在 Line (1) 中,length() 被 codePointCount() 替代。其次,在 Lines (3)、(4) 和 (6) 中,char 类型的索引通过 offsetByCodePoints() 用码位索引替代。</p>
<p>基本的算法流与 示例 2-1 中的看起来几乎一样。但处理时间根据字符串长度与示例 2-1 的比率同比增加,因为 offsetByCodePoints() 总是从字符串头到指定索引计算字符串内部。</p>
<p><strong>示例 2-3:减少的处理时间</strong></p>
<p>可以使用清单 10 中展示的方法来避免 示例 2-2 的性能问题:</p>
<p><strong>清单 10. 改进的性能</strong></p>
<pre class="brush:php;toolbar:false">String[] sliceString(String str, int width) { // Example 2-3      // It must be that "str != null && width > 0".      List<string> slices = new ArrayList<string>();      int len = str.length(); // (1) the length of str      int sliceLimit          // (2) Do not slice beyond here. [Modified]              = (len >= width * 2 || str.codePointCount(0, len) > width)              ? str.offsetByCodePoints(len, -width) : 0;      int pos = 0;            // the current position per char type       while (pos <p>首先,在 Line (2) 中,(清单 9 中的)表达式 len-width 被 offsetByCodePoints(len,-width) 替代。但是,当 width 的值大于码位的数量时,这会抛出一个 IndexOutOfBoundsException 实例。必须考虑边界条件以避免异常,使用一个带有 try/catch 异常处理程序的子句将是另一个解决方案。如果表达式 len>width*2 为 true,则可以安全地调用 offsetByCodePoints(),因为即使所有码位都被转换为代理对,码位的数量仍会超过 width 的值。或者,如果 codePointCount(0,len)>width 为 true,也可以安全地调用 offsetByCodePoints()。如果是其他情况,sliceLimit 必须设置为 0。</p>
<p>在 Line (4) 中,清单 9 中的表达式 pos + width 必须在 while 循环中使用 offsetByCodePoints(pos,width) 替换。需要计算的量位于 width 的值中,因为第一个参数指定当 width 的值。接下来,在 Line (5) 中,表达式 pos+=width 必须使用表达式 pos=end 替换。这避免两次调用 offsetByCodePoints() 来计算相同的索引。源代码可以被进一步修改以最小化处理时间。</p>
<h3 id="yisu3h-to116">处理时间比较</h3>
<p>图 1 和图 2 展示了示例 2-1、2-2 和 2-3 的处理时间。样例字符串包含相同数量的代理对和非代理对。当字符串的长度和 width 的值被更改时,样例字符串被切割 10,000 次。</p>
<p><img src="https://img.php.cn/upload/article/000/000/164/168337700040575.png" alt="So verwenden Sie die Unicode-Agentenprogrammierung in Java"></p>
<p><strong>图 1. 一个分段的常量宽度</strong></p>
<p><img src="https://img.php.cn/upload/article/000/000/164/168337700041405.png" alt="So verwenden Sie die Unicode-Agentenprogrammierung in Java"></p>
<p><strong>图 2. 分段的常量计数</strong></p>
<p>示例 2-1 和 2-3 按照长度比例增加了它们的处理时间,但 示例 2-2 按照长度的平方比例增加了处理时间。当字符串长度和 width 的值增加而分段的数量固定时,示例 2-1 拥有一个常量处理时间,而示例 2-2 和 2-3 以 width 的值为比例增加了它们的处理时间。</p>
<h3 id="yisu3h-to127">信息 API</h3>
<p>大多数处理代理的信息 API 拥有两种名称相同的方法。一种接收 16 位 char 类型参数,另一种接收 32 为码位参数。表 2 展示了每个 API 的返回值。第三列针对 U+53F1,第 4 列针对 U+20B9F,最后一列针对 U+D842(即高代理),而 U+20B9F 被转换为 U+D842 加上 U+DF9F 的代理对。如果程序不能处理代理对,则值 U+D842 而不是 U+20B9F 将导致意想不到的结果(在表 2 中以粗斜体表示)。</p>
<p><strong>表 2. 用于代理的信息 API</strong></p>
<p><img src="https://img.php.cn/upload/article/000/000/164/168337700086359.gif" alt="So verwenden Sie die Unicode-Agentenprogrammierung in Java"></p>
<h3 id="yisu3h-to133">其他 API</h3>
<p>本小节介绍前面的小节中没有讨论的代理对相关 API。表 3 展示所有这些剩余的 API。所有代理对 API 都包含在表 1、2 和 3 中。</p>
<p><strong>表 3. 其他代理 API</strong></p>
<p><img src="https://img.php.cn/upload/article/000/000/164/168337700025390.gif" alt="So verwenden Sie die Unicode-Agentenprogrammierung in Java"></p>
<p>清单 11 展示了从一个码位创建一个字符串的 5 种方法。用于测试的码位是 U+53F1 和 U+20B9F,它们在一个字符串中重复了 100 亿次。清单 11 中的注释部分显示了处理时间:</p>
<p><strong>清单 11. 从一个码位创建一个字符串的 5 种方法</strong></p>
<pre class="brush:php;toolbar:false">int cp = 0x20b9f; // CJK Ideograph Extension B  String str1 = new String(new int[]{cp}, 0, 1);    // processing time: 206ms  String str2 = new String(Character.toChars(cp));                  //  187ms  String str3 = String.valueOf(Character.toChars(cp));              //  195ms  String str4 = new StringBuilder().appendCodePoint(cp).toString(); //  269ms  String str5 = String.format("%c", cp);                            // 3781ms

str1、str2、str3 和 str4 的处理时间没有明显不同。相反,创建 str5 花费的时间要长得多,因为它使用 String.format(),该方法支持基于本地和格式化信息的灵活输出。str5 方法应该只用于程序的末尾来输出文本。

Das obige ist der detaillierte Inhalt vonSo verwenden Sie die Unicode-Agentenprogrammierung in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen