Kleinste freie Zahl

@CyborgBeta ich kann leider blos erstmal den alten posten,jeddoch hier ist er:


import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign;
import org.bukkit.plugin.java.JavaPlugin;

import de.Blackbird.MinigameAPI.MinigameAPI;
import de.Blackbird.MinigameAPI.Database.Operators.DataTypes;
import de.Blackbird.MinigameAPI.Database.Table;
import de.Blackbird.MinigameAPI.Player.MinigamePlayer;
import de.Blackbird.MinigameAPI.Util.LocationSerialiser;
import de.Blackbird.WirlessRedstone.Listeners.BlockPlaceListener;
import de.Blackbird.WirlessRedstone.Listeners.BlockRedstoneListener;
import de.Blackbird.WirlessRedstone.Technique.Receiver;
import de.Blackbird.WirlessRedstone.Technique.Sender;

public class WirlessRedstone extends JavaPlugin {

	private static WirlessRedstone instance;
	private Map<Integer,Sender> senderMap;
	private Map<Integer,Receiver> receiverMap;
	private Map<Sender,Receiver> connectedMap;
	private Table wrTable;
	
	
	@Override
	public void onEnable() {
		System.out.println("Loading WirlessRedstone");
		instance = this;
		senderMap = new HashMap<Integer,Sender>();
		receiverMap = new HashMap<Integer,Receiver>();
		connectedMap = new HashMap<Sender,Receiver>();
		wrTable = MinigameAPI.getInstance().getDatabaseAPI().getTable("WirlessRedstone");
		Bukkit.getPluginManager().registerEvents(new BlockPlaceListener(), this);
		Bukkit.getPluginManager().registerEvents(new BlockRedstoneListener(), this);
		try {
			MinigameAPI.getInstance().getLanguageAPI().addLangString("WR_ID_NOT_AVIABLE");
			MinigameAPI.getInstance().getLanguageAPI().reloadLocale();
		} catch (Exception e1) {
			this.getLogger().severe("Konnte WirlessRedstone nicht starten(Language)");

		}

		try {
			checkTable();
		} catch (SQLException e) {
			this.getLogger().severe("Konnte WirlessRedstone nicht starten(DB)");
		}
		executeSignUpdate();
		System.out.println("WirlessRedstone Loaded!");
	}
	
	@Override
	public void onDisable() {
		System.out.println("Disabling WirlessRedstone");
		try {
			saveDatabase();
		} catch (SQLException e) {
			this.getLogger().severe("Konnte WirlessRedstone nicht stoppen(DB)");
		}
		System.out.println("WirlessRedstone Disabled!");
	}
	
	private void checkTable() throws SQLException {
		if(!wrTable.exists()) {
			wrTable.create(new String[] {
				"Server",
				"Type",
				"LocString",
				"ID",
				"isEnabled"
			}, new DataTypes[] {
				DataTypes.Text,
				DataTypes.Text,
				DataTypes.Text,
				DataTypes.Int,
				DataTypes.Text
			});
		}
	}
	
	private void saveDatabase() throws SQLException {
		connectedMap.clear();
		for(Entry<Integer, Sender> entry : senderMap.entrySet()) {
			wrTable.deleteEntry("ID", entry.getKey(),"Server",MinigameAPI.getInstance().getServerName(),"Type","Sender");
			if(!wrTable.contains("ID", entry.getKey(), "Type", "Sender","Server",MinigameAPI.getInstance().getServerName())) {
				wrTable.insertEntry(new String[] {
						"Server",
						"Type",
						"LocString",
						"ID",
						"isEnabled"
				}, new Object[] {
				MinigameAPI.getInstance().getServerName(),	
				"Sender",
				entry.getValue().getLocationString(),
				entry.getValue().getID(),
				String.valueOf(entry.getValue().isEnabled())
				});
				return;
			}
		}
		senderMap.clear();
		for(Entry<Integer, Receiver> entry : receiverMap.entrySet()) {
			wrTable.deleteEntry("ID", entry.getKey(),"Server",MinigameAPI.getInstance().getServerName(),"Type","Receiver");
			if(!wrTable.contains("ID", entry.getKey(), "Type", "Receiver","Server",MinigameAPI.getInstance().getServerName())) {
				wrTable.insertEntry(new String[] {
						"Server",
						"Type",
						"LocString",
						"ID",
						"isEnabled"
				}, new Object[] {
				MinigameAPI.getInstance().getServerName(),	
				"Receiver",
				entry.getValue().getLocationString(),
				entry.getValue().getID(),
				String.valueOf(entry.getValue().isEnabled())
				});
				return;
			}
		}
		receiverMap.clear();
	}
	public static Sender getSender(int id) {
		return instance.senderMap.get(id);
	}
	public static Receiver getReceiver(int id) {
		return instance.receiverMap.get(id);
	}
	public static Map<Sender,Receiver> getConnectedMap() {
		return instance.connectedMap;
	}
	public static Table getDatabaseTable() {
		return instance.wrTable;
	}
	public static void addSender(Sender sender) {
		instance.senderMap.put(sender.getID(), sender);
	}
	public static void removeSender(int id) {
		instance.senderMap.remove(id);
	}
	public static void addReceiver(Receiver receiver) {
		instance.receiverMap.put(receiver.getID(), receiver);
	}
	public static void removeReceiver(int id) {
		instance.receiverMap.remove(id);
	}
	public static int getFreeSenderID() {
		int id = 1;
		boolean hasFound = false;
		while(!hasFound) {
			if(!containsSenderID(id)) {
				hasFound = true;
				break;
			}
			id++;
		}
		return id;
	}
	public static int getFreeReceiverID() {
		int id = 1;
		boolean hasFound = false;
		while(!hasFound) {
			if(!containsReceiverID(id)) {
				hasFound = true;
				break;
			}
			id++;
		}
		return id;
	}
	public static boolean containsSenderID(int id) {
		Set<Integer> allIds = instance.senderMap.keySet();
		return allIds.contains(id);
	}
	public static boolean containsReceiverID(int id) {
		Set<Integer> allIds = instance.receiverMap.keySet();
		return allIds.contains(id);
	}
	public static void registerNewSign(Sign s,MinigamePlayer p) {
		if(s.getLine(0).equals("[Receiver]") && p.hasPermission("Admin")) {
			instance.registerReceiver(s,p);
		}
		if(s.getLine(0).equals("[Sender]") && p.hasPermission("Admin")) {
			instance.registerSender(s,p);
		}
	}
	public void registerReceiver(Sign s , MinigamePlayer p) {
		int id = 1;
		try {
			id = Integer.valueOf(s.getLine(1));
		}
		catch(NumberFormatException e) {
			id = getFreeReceiverID();
		}
		s.setLine(1, String.valueOf(id));
		receiverMap.put(id, new Receiver(LocationSerialiser.locationEntityToString(s.getLocation()), id));
		executeSignUpdate();
	}
	public static int getHighestSenderID() {
		Set<Integer> senderSet = instance.senderMap.keySet();
		int highest = 1;
		while(senderSet.iterator().hasNext()) {
			int puff = senderSet.iterator().next();
			if(puff > highest) {
				highest = puff;
			}
		}
		return highest;
	}
	public static int getHighestReceiverID() {
		Set<Integer> receiverSet = instance.receiverMap.keySet();
		int highest = 1;
		while(receiverSet.iterator().hasNext()) {
			int puff = receiverSet.iterator().next();
			if(puff > highest) {
				highest = puff;
			}
		}
		return highest;
	}
	public void registerSender(Sign s, MinigamePlayer p) {
		int id = 1;
		try {
			id = Integer.valueOf(s.getLine(1));
			if(containsSenderID(id)) {
				p.sendTranslatedMessage("WR_ID_NOT_AVIABLE");
				return;
			}
		}
		catch(NumberFormatException e) {
			id = getFreeSenderID();
		}
		s.setLine(1, String.valueOf(id));
		senderMap.put(id, new Sender(id, LocationSerialiser.locationEntityToString(s.getLocation())));
		executeSignUpdate();
	}
	public static void executeSignUpdate() {
		instance.checkConnectedMap();
	}
	private void checkConnectedMap() {
		int highestSenderID = getHighestSenderID();
		int highestReceiverID = getHighestReceiverID();
		connectedMap.clear();
		
		for(int i=1; i < highestSenderID;i++) {
			if(i > highestReceiverID) break;
			if(containsReceiverID(i) && containsSenderID(i)) {
				Sender sender = getSender(i);
				Receiver receiver = getReceiver(i);
				sender.setReceiver(receiver);
				receiver.setReceiver(sender);
				sender.setEnabled(true);
				receiver.setEnabled(true);
				connectedMap.put(sender, receiver);
			}
		}
	}
	@SuppressWarnings("deprecation")
	public static void setSenderPowerd(Sender s,boolean powered) {
		if(s.hasReceiver() && s.isEnabled() && powered) {
			Block receiver = s.getReceiver().getLocation().getBlock();
			Block south = receiver.getRelative(BlockFace.SOUTH);
			Block north = receiver.getRelative(BlockFace.NORTH);
			Block east = receiver.getRelative(BlockFace.EAST);
			Block west = receiver.getRelative(BlockFace.WEST);
			if(south.getType() == Material.REDSTONE_WIRE) {
				south.setData((byte)15, true);
			}
			if(north.getType() == Material.REDSTONE_WIRE) {
				north.setData((byte)15, true);
			}
			if(east.getType() == Material.REDSTONE_WIRE) {
				east.setData((byte)15, true);
			}
			if(west.getType() == Material.REDSTONE_WIRE) {
				west.setData((byte)15, true);
			}
		}
		if(s.hasReceiver() && s.isEnabled() && !powered) {
			Block receiver = s.getReceiver().getLocation().getBlock();
			Block south = receiver.getRelative(BlockFace.SOUTH);
			Block north = receiver.getRelative(BlockFace.NORTH);
			Block east = receiver.getRelative(BlockFace.EAST);
			Block west = receiver.getRelative(BlockFace.WEST);
			if(south.getType() == Material.REDSTONE_WIRE) {
				south.setData((byte)15, false);
			}
			if(north.getType() == Material.REDSTONE_WIRE) {
				north.setData((byte)15, false);
			}
			if(east.getType() == Material.REDSTONE_WIRE) {
				east.setData((byte)15, false);
			}
			if(west.getType() == Material.REDSTONE_WIRE) {
				west.setData((byte)15, false);
			}
		}
	}

}

So hart es auch klingen mag, aber der Code ist dilletantisch. Angefangen bei der Tatsache, dass man ein Singleton ("**instance[/]") schon gar nicht mehr initialisieren sollte, während bereits eine Objektmethode (“onLoad()”) aufgerufen wird, bis hin zu der überaus eigensinnigen Paketeinteilung. Das lässt alles in allem darauf schliessen, dass du dich mehr mit Bukkit befasst, als mit Java.

Wie ich es mir dachte, ist eine ID jeweils ein Member der Klassen Sender und Receiver aus welchem man sie mit einem Getter bekommt. Warum also diese IDs ausserhalb dieser Klassen erstellen und per Konstruktor an diese übergeben?

  private static final Bitset IDs = new BitSet();

  private final int id;
  private final String name;

  public Sender(String name) {
    this.name = name;
    synchronized(IDs) {
      id = IDs.nextClearBit(0);
      IDs.set(id);
    }
  }

  public void dispose() {
    IDs.clear(id);
  }

  protected void finalize() throws Throwable {
    super.finalize();
    IDs.clear(id);
  }
}```
macht die Sache viel einfacher. Wenn man dann beim Verwerfen eines Receivers und/oder eines Empfängers peinlich darauf achtet, die jeweiligen "dispose()"-Methode aufzurufen, kann man sich sogar das Überschreiben von "finalize()" schenken, denn merke: Wenn "finalize()" überschrieben wurde, landen die Objekte vor einem GC-Run nur in einem anderen Speicherbereich, welcher erst freigegeben wird, wenn die JVM beendet wurde oder sämtliche Finalizer fehlerfrei durchgelaufen sind.

Nun kann man aus Sender und Receiver Bezüge aufbauen und zwar:
```private static final Map<Sender, Receiver> SENDER_TO_RECEIVER = new IdentityHashMap<Sender, Receiver>();
private static final Map<Receiver, Sender> RECEIVER_TO_SENDER = new IdentityHashMap<Receiver, Sender>();```
für Einzelbezüge - Jeder Receiver kann nur einen Sender haben und umgekehrt - oder
```private static final Map<Sender, Set<Receiver>> SENDER_TO_RECEIVER = new IdentityHashMap<Sender, Set<Receiver>>();
private static final Map<Receiver, Set<Sender>> RECEIVER_TO_SENDER = new IdentityHashMap<Receiver, Set<Sender>>();```
für Mehrfachbezüge - Jeder Sender kann mehrere Receiver haben und jeder Receiver kann bei mehreren Sendern angemeldet sein.

Eine IdentityHashMap verwende ich hier, damit man hashCode und equals evtl. überschreiben kann und sich die Sache dadurch nicht ändert. Eine normale HashMap tut es natürlich auch, man kann ja die ID zum Vergleichen und für den Hashcode verwenden.
Für die Mehrfachbezüge wird aber auf jeden Fall mindestens ein HashSet fällig, es sei denn, man implementiert sich ein entsprechendes IdentityHashSet selber. Natürlich müssen sowohl bei Einzel- als auch bei Mehrfachbezügen beide Maps gleichermassen gepflegt werden.

Danke. Ja ich bin noch Java Anfänger, deshalb suche ich ja auch Hilfe

Für “Mehrfachbezüge” eignet sich eine Multimap besser als eine Map<?, Set<?>>.
Die Verwendung der IdentityHashMap kann ich überdies nicht nachvollziehen. Rein prophylaktisch sollte man sie nicht einsetzen. Standardmäßig vergleicht equals die Identität zweier Objekte anhand ihrer Objektreferenz. Nur wenn man equals überschreibt, ändert sich das Verhalten. Wenn man das tut, muss man sich über die Konsequenzen im Klaren sein - das passiert nicht aus Versehen.

[QUOTE=cmrudolph]Die Verwendung der IdentityHashMap kann ich überdies nicht nachvollziehen.[/QUOTE]Die Begründung steht doch dabei. Wenn man sich dazu entschließt, hashCode und equals nachträglich zu implementieren und dabei die IDs ausser acht lässt oder lassen will, kann man dies tun, ohne das es auf die restliche Implementation Einfluss nimmt. IdentityHashMap/Set verwende ich überwiegend dort, wo ich Mutables oder Klassen, die hashCode und equals nicht überschreiben, als Keys verwende.

MultiMap ist eine Möglichkeit. Ich weis aber nicht, ob es sinnvoll ist, Anfänger, die sich weniger mit Java als mit speziellen APIs auseinandersetzen wollen, mit n weiteren APIs zu belasten. Der Tipp, sich zunächst eindringlicher mit Java zu befassen und sich erst dann an kompliziertere Projekte zu wagen, bleibt aber gültig.

Die habe ich auch verstanden. Wenn man sich die Dokumentation zur Implementierung ansieht, fällt als erstes ein fett geschriebener Satz auf:

This class is not a general-purpose Map implementation! While this class implements the Map interface, it intentionally violates Map’s general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required.

Einen Anfänger mit so einer sich nicht standardmäßig verhaltenden Implementierung zu konfrontieren halte ich für äußerst schlecht.