Waarom implementeert de klasse String in Java Iterable niet?

Veel Java-frameworkklassen implementeren Iterable, maar Stringniet. Het is logisch om tekens in een Stringte herhalen, net zoals men items in een gewone array kan herhalen.

Is er een reden waarom StringIterableniet implementeert?


Antwoord 1, autoriteit 100%

Er is niet echt een goed antwoord. Een iterator in Java is specifiek van toepassing op een verzameling discrete items (objecten). Je zou denken dat een String, die CharSequenceimplementeert, een “verzameling” van discrete karakters zou moeten zijn. In plaats daarvan wordt het behandeld als een enkele entiteit die toevallig uit tekens bestaat.

In Java lijkt het erop dat iterators alleen echt worden toegepast op verzamelingen en niet op een string. Er is geen reden waarom het op deze manier is (in de buurt van zoals ik kan zien – je zou waarschijnlijk met Gosling of de API-schrijvers moeten praten); het lijkt een conventie of een ontwerpbeslissing te zijn. Er is inderdaad niets dat verhindertCharSequenceom Iterablete implementeren.

Dat gezegd hebbende, je kunt de karakters in een string herhalen als volgt:

for (int i = 0; i < str.length(); i++) {
  System.out.println(str.charAt(i));
}

Of:

for(char c : str.toCharArray()) {
  System.out.println(c);
}

Of:

"Java 8".chars().forEach(System.out::println);

Houd er rekening mee dat u een teken van een String op zijn plaats niet kunt wijzigen, omdat Strings onveranderlijk zijn. De veranderlijke aanvulling op een String is StringBuilder (of de oudere StringBuffer).

BEWERKEN

Ter verduidelijking op basis van de opmerkingen bij dit antwoord. Ik probeer een mogelijke redenuit te leggen waarom er geen Iterator is op een String. Ik probeer niet te zeggen dat het niet mogelijk is; inderdaad, ik denk dat het logisch zou zijn dat CharSequenceIterableimplementeert.

Stringbiedt CharSequence, die, al is het maar conceptueel, verschilt van een String. Een Stringwordt meestal gezien als een enkele entiteit, terwijl CharSequenceprecies dat is: een reeks tekens. Het zou logisch zijn om een ​​iterator te hebben op een reeks tekens (d.w.z. op CharSequence), maar niet alleen op een Stringzelf.

Zoals Foxfire terecht heeft opgemerkt in de opmerkingen, implementeert Stringde CharSequence-interface, dus typegewijs is een Stringeen CharSequence. Semantisch gezien lijkt het mij dat het twee aparte dingen zijn – ik ben hier waarschijnlijk pedant, maar als ik aan een Stringdenk, denk ik er meestal aan als een enkele entiteit die toevallig uit karakters bestaat . Denk aan het verschil tussen de reeks cijfers 1, 2, 3, 4en het getal 1234. Beschouw nu het verschil tussen de tekenreeks abcden de reeks tekens a, b, c, d. Ik probeer dit verschil aan te geven.

Naar mijn mening is vragen waarom Stringgeen iterator heeft hetzelfde als vragen waarom Integergeen iterator heeft zodat je de afzonderlijke cijfers kunt herhalen .


Antwoord 2, autoriteit 46%

De reden is simpel: de tekenreeksklasse is veel ouder dan Iterable.

En natuurlijk heeft niemand ooit de interface aan String willen toevoegen (wat enigszins vreemd is omdat het CharSequence implementeert dat op precies hetzelfde idee is gebaseerd).

Het zou echter enigszins imperformant zijn omdat Iterable een object retourneert. Het zou dus elke teruggestuurde Char moeten verpakken.

Bewerken: net als vergelijking: .Net ondersteunt het opsommen op String, maar in .Net werkt Iterable ook op native typen, dus er is geen inwikkeling vereist zoals dit in Java vereist zou zijn.


Antwoord 3, autoriteit 43%

Voor wat het waard is, mijn collega Josh Bloch wil deze functie ten zeerste aan Java 7 toevoegen:

for (char c : aString) { ... }

en

for (int codePoint : aString) { ... }

Dit zou de gemakkelijkste manier zijn om tekens en logische tekens (codepunten) ooit te doorlopen. Het zou niet nodig zijn om StringIterablete laten implementeren, wat boksen zou forceren.

Zonder die taalfunctie zal er geen echt goed antwoord op dit probleem zijn. En hij lijkt erg optimistisch dat hij dit kan laten gebeuren, maar ik weet het niet zeker.


Antwoord 4, autoriteit 11%

Ze zijn het gewoon vergeten te doen.


Antwoord 5, autoriteit 7%

Een van de belangrijkste redenen om String Iterable te implementeren, is om de eenvoudige for(each) lus in te schakelen, zoals hierboven vermeld. Dus een reden om String niet Iterable te implementeren, kan de inherente inefficiëntie van een naïeve implementatie zijn, omdat het resultaat moet worden ingekaderd. Als de implementatie van de resulterende Iterator (zoals geretourneerd door String.iterator()) echter definitief is, kan de compiler deze in een speciaal geval plaatsen en byte-code genereren zonder boxing/unboxing.


Antwoord 6, autoriteit 4%

Als je echt geïnteresseerd bent om hier te herhalen:

String str = "StackOverflow";
for (char c: str.toCharArray()){
     //here you go
}

Antwoord 7

Ik weet niet zeker waarom dit in 2020 nog steeds niet is geïmplementeerd, ik vermoed dat Strings veel speciale behandeling krijgen in Java (waarbij de compiler de operator +overbelast voor het samenvoegen van strings, letterlijke tekenreeksen, tekenreeksconstanten opgeslagen in een gemeenschappelijke pool, enz.) dat deze functie misschien moeilijker te implementeren is dan het lijkt (of dat het te veel dingen in de war brengt om de moeite waard te zijn vanuit het oogpunt van de uitvoerders).

Aan de andere kant is het implementeren van iets dat in de buurt komt niet al te veel werk. Ik wilde dit in een van mijn projecten, dus schreef ik deze eenvoudige lessen:

public class CharIterable implements Iterable<Character> {
  public CharIterable(CharSequence seq) {
    this.seq = seq;
  }
  @Override
  public Iterator<Character> iterator() {
    return new CharIterator(seq);
  }
  private final CharSequence seq;
}
public class CharIterator implements Iterator<Character> {
  public CharIterator(CharSequence sequence) {
    this.sequence = sequence;
  }
  @Override
  public synchronized boolean hasNext() {
    return position < sequence.length();
  }
  @Override
  public synchronized Character next() {
    return sequence.charAt(position++);
  }
  /**
   * Character sequence to iterate over
   */
  private final CharSequence sequence;
  /**
   * Current position of iterator which is the position of the item that
   * will be returned by {@link #next()}.
   */
  private int position = 0;
}

Hiermee kan ik dit doen:

for (Character c: new CharIterable("This is a test")) {
  \\ do something with c
}

Dit lijkt veel voor zoiets eenvoudigs, maar het zorgt er dan voor dat strings worden behandeld als een itereerbare reeks tekens en transparant werken met methoden die zijn ontworpen om te werken aan verzamelingen van dingen (lijsten, sets, enz.).


Antwoord 8

Iterablevan wat? Iterable<Integer>zou het meest logisch zijn, waarbij elk element een Unicode-codepunt vertegenwoordigt. Zelfs Iterable<Character>zou traag en zinloos zijn als we toCharArrayhebben.

Other episodes